Make PATH search do the right thing w/ empty path

This commit is contained in:
Justine Tunney 2021-01-29 01:27:09 -08:00
parent a5f3456333
commit caee314a50
7 changed files with 147 additions and 40 deletions

View file

@ -27,7 +27,9 @@ static bool AccessCommand(char path[hasatleast PATH_MAX], const char *name,
size_t namelen, size_t pathlen) { size_t namelen, size_t pathlen) {
if (pathlen + 1 + namelen + 1 + 4 + 1 > PATH_MAX) return -1; if (pathlen + 1 + namelen + 1 + 4 + 1 > PATH_MAX) return -1;
if (pathlen && (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\')) { if (pathlen && (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\')) {
path[pathlen++] = '/'; path[pathlen] =
!IsWindows() ? '/' : memchr(path, '\\', pathlen) ? '\\' : '/';
pathlen++;
} }
memcpy(path + pathlen, name, namelen + 1); memcpy(path + pathlen, name, namelen + 1);
if (isexecutable(path)) return true; if (isexecutable(path)) return true;
@ -43,15 +45,18 @@ static bool SearchPath(char path[hasatleast PATH_MAX], const char *name,
size_t i; size_t i;
const char *p; const char *p;
p = firstnonnull(emptytonull(getenv("PATH")), "/bin:/usr/local/bin:/usr/bin"); p = firstnonnull(emptytonull(getenv("PATH")), "/bin:/usr/local/bin:/usr/bin");
for (;; p += i) { for (;;) {
while (*p == ':' || *p == ';') ++p; for (i = 0; p[i] && p[i] != ':' && p[i] != ';'; ++i) {
if (!*p) break; if (i < PATH_MAX) path[i] = p[i];
for (i = 0; i < PATH_MAX && p[i] && p[i] != ':' && p[i] != ';'; ++i) {
path[i] = p[i];
} }
if (AccessCommand(path, name, namelen, i)) { if (AccessCommand(path, name, namelen, i)) {
return true; return true;
} }
if (p[i] == ':' || p[i] == ';') {
p += i + 1;
} else {
break;
}
} }
return false; return false;
} }
@ -71,9 +76,12 @@ char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) {
int rc, olderr; int rc, olderr;
if (!(namelen = strlen(name))) return PROGN(enoent(), NULL); if (!(namelen = strlen(name))) return PROGN(enoent(), NULL);
if (namelen + 1 > PATH_MAX) return PROGN(enametoolong(), NULL); if (namelen + 1 > PATH_MAX) return PROGN(enametoolong(), NULL);
if (name[0] == '/' || name[0] == '\\') { if (strchr(name, '/') || strchr(name, '\\')) {
memcpy(pathbuf, name, namelen + 1); if (AccessCommand(strcpy(pathbuf, ""), name, namelen, 0)) {
return pathbuf; return pathbuf;
} else {
return NULL;
}
} }
olderr = errno; olderr = errno;
if ((IsWindows() && if ((IsWindows() &&
@ -81,7 +89,6 @@ char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) {
stpcpy(pathbuf, kNtSystemDirectory) - pathbuf) || stpcpy(pathbuf, kNtSystemDirectory) - pathbuf) ||
AccessCommand(pathbuf, name, namelen, AccessCommand(pathbuf, name, namelen,
stpcpy(pathbuf, kNtWindowsDirectory) - pathbuf))) || stpcpy(pathbuf, kNtWindowsDirectory) - pathbuf))) ||
AccessCommand(strcpy(pathbuf, ""), name, namelen, 0) ||
SearchPath(pathbuf, name, namelen)) { SearchPath(pathbuf, name, namelen)) {
errno = olderr; errno = olderr;
return pathbuf; return pathbuf;

View file

@ -46,6 +46,8 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
const struct StackFrame *frame; const struct StackFrame *frame;
const char *debugbin, *p1, *p2, *p3, *addr2line; const char *debugbin, *p1, *p2, *p3, *addr2line;
char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames]; char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames];
if (IsOpenbsd()) return -1;
if (IsWindows()) return -1;
if (!(debugbin = FindDebugBinary()) || !(addr2line = GetAddr2linePath())) { if (!(debugbin = FindDebugBinary()) || !(addr2line = GetAddr2linePath())) {
return -1; return -1;
} }

58
libc/x/rmrf.c Normal file
View file

@ -0,0 +1,58 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/dirent.h"
#include "libc/calls/struct/stat.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/dt.h"
#include "libc/x/x.h"
static int rmrfdir(const char *dirpath) {
int rc;
DIR *d;
char *path;
struct dirent *e;
if (!(d = opendir(dirpath))) return -1;
for (rc = 0; (e = readdir(d));) {
if (!strcmp(e->d_name, ".")) continue;
if (!strcmp(e->d_name, "..")) continue;
if (strchr(e->d_name, '/')) abort();
path = xjoinpaths(dirpath, e->d_name);
if ((e->d_type == DT_DIR ? rmrfdir(path) : unlink(path)) == -1) {
free(path);
closedir(d);
return -1;
}
free(path);
}
return closedir(d);
}
/**
* Recursively removes file or directory.
*/
int rmrf(const char *path) {
struct stat st;
if (stat(path, &st) == -1) return -1;
if (!S_ISDIR(st.st_mode)) return unlink(path);
return rmrfdir(path);
}

View file

@ -45,9 +45,15 @@ char *xstrdup(const char *) _XPNN _XMAL;
char *xstrndup(const char *, size_t) _XPNN _XMAL; char *xstrndup(const char *, size_t) _XPNN _XMAL;
char *xstrcat(const char *, ...) paramsnonnull((1)) nullterminated() _XMAL; char *xstrcat(const char *, ...) paramsnonnull((1)) nullterminated() _XMAL;
char *xstrmul(const char *, size_t) paramsnonnull((1)) _XMAL; char *xstrmul(const char *, size_t) paramsnonnull((1)) _XMAL;
char *xinet_ntop(int, const void *) _XPNN _XMAL;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § eXtended apis » files
*/
int rmrf(const char *);
char *xdirname(const char *) paramsnonnull() _XMAL; char *xdirname(const char *) paramsnonnull() _XMAL;
char *xjoinpaths(const char *, const char *) paramsnonnull() _XMAL; char *xjoinpaths(const char *, const char *) paramsnonnull() _XMAL;
char *xinet_ntop(int, const void *) _XPNN _XMAL;
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § eXtended apis » time cosmopolitan § eXtended apis » time

View file

@ -34,6 +34,7 @@ LIBC_X_A_CHECKS = \
LIBC_X_A_DIRECTDEPS = \ LIBC_X_A_DIRECTDEPS = \
LIBC_CALLS \ LIBC_CALLS \
LIBC_CALLS_HEFTY \
LIBC_FMT \ LIBC_FMT \
LIBC_INTRIN \ LIBC_INTRIN \
LIBC_MEM \ LIBC_MEM \

View file

@ -16,52 +16,81 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/struct/dirent.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/log/check.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/runtime/gc.h" #include "libc/runtime/gc.h"
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/dt.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/x/x.h" #include "libc/x/x.h"
uint64_t i; uint64_t i;
char pathbuf[PATH_MAX]; char pathbuf[PATH_MAX];
const char *oldpath, *bindir, *homedir, *binsh, *sh; const char *testdir, *oldpath;
TEST(commandv_00, todo) { /* TODO(jart): Improve this on Windows. */ void SetUp(void) {
if (IsWindows()) exit(0);
}
TEST(commandv_001, setupFiles) {
mkdir("o", 0755); mkdir("o", 0755);
mkdir("o/tmp", 0755); mkdir("o/tmp", 0755);
oldpath = strdup(getenv("PATH")); testdir = xasprintf("o/tmp/%s.%d", program_invocation_short_name, getpid());
homedir = xasprintf("o/tmp/home.%d", getpid()); mkdir(testdir, 0755);
bindir = xasprintf("o/tmp/bin.%d", getpid()); CHECK_NE(-1, chdir(testdir));
binsh = xasprintf("%s/sh.com", bindir); mkdir("bin", 0755);
ASSERT_NE(-1, mkdir(homedir, 0755)); mkdir("home", 0755);
ASSERT_NE(-1, mkdir(bindir, 0755)); oldpath = strdup(nulltoempty(getenv("PATH")));
ASSERT_NE(-1, touch(binsh, 0755)); CHECK_NE(-1, setenv("PATH", "bin", true));
ASSERT_NE(-1, setenv("PATH", bindir, true));
} }
TEST(commandv_010, testSlashes_wontSearchPath_butChecksAccess) { void TearDown(void) {
sh = defer(unlink, gc(xasprintf("%s/sh.com", homedir))); CHECK_NE(-1, setenv("PATH", oldpath, true));
EXPECT_NE(-1, touch(sh, 0755)); CHECK_NE(-1, chdir("../../.."));
CHECK_NE(-1, rmrf(testdir));
}
TEST(commandv, testPathSearch) {
EXPECT_NE(-1, touch("bin/sh", 0755));
EXPECT_STREQ("bin/sh", commandv("sh", pathbuf));
}
TEST(commandv, testPathSearch_appendsComExtension) {
EXPECT_NE(-1, touch("bin/sh.com", 0755));
EXPECT_STREQ("bin/sh.com", commandv("sh", pathbuf));
}
TEST(commandv, testSlashes_wontSearchPath_butChecksAccess) {
EXPECT_NE(-1, touch("home/sh", 0755));
i = g_syscount; i = g_syscount;
EXPECT_STREQ(sh, commandv(sh, pathbuf)); EXPECT_STREQ("home/sh", commandv("home/sh", pathbuf));
if (!IsWindows()) EXPECT_EQ(i + 1 /* access() */, g_syscount); if (!IsWindows()) EXPECT_EQ(i + 1, g_syscount);
} }
TEST(commandv_999, teardown) { TEST(commandv, testSlashes_wontSearchPath_butStillAppendsComExtension) {
setenv("PATH", oldpath, true); EXPECT_NE(-1, touch("home/sh.com", 0755));
unlink(binsh); i = g_syscount;
rmdir(bindir); EXPECT_STREQ("home/sh.com", commandv("home/sh", pathbuf));
rmdir(homedir); if (!IsWindows()) EXPECT_EQ(i + 2, g_syscount);
free(bindir); }
free(binsh);
free(homedir); TEST(commandv, testSameDir_doesntHappenByDefault) {
free(oldpath); EXPECT_NE(-1, touch("bog", 0755));
EXPECT_EQ(NULL, commandv("bog", pathbuf));
}
TEST(commandv, testSameDir_willHappenWithColonBlank) {
CHECK_NE(-1, setenv("PATH", "bin:", true));
EXPECT_NE(-1, touch("bog", 0755));
EXPECT_STREQ("bog", commandv("bog", pathbuf));
}
TEST(commandv, testSameDir_willHappenWithColonBlank2) {
CHECK_NE(-1, setenv("PATH", ":bin", true));
EXPECT_NE(-1, touch("bog", 0755));
EXPECT_STREQ("bog", commandv("bog", pathbuf));
} }

View file

@ -393,7 +393,11 @@ int Serve(void) {
if (!Poll() && (!g_timeout || g_interrupted)) break; if (!Poll() && (!g_timeout || g_interrupted)) break;
} }
close(g_servfd); close(g_servfd);
LOGF("timeout expired, shutting down"); if (!g_timeout) {
LOGF("timeout expired, shutting down");
} else {
LOGF("got ctrl-c, shutting down");
}
return 0; return 0;
} }