mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-23 13:52:28 +00:00
Fix race condition in makedirs()
This commit is contained in:
parent
b7c07d548c
commit
571c2c3c69
7 changed files with 90 additions and 10 deletions
Binary file not shown.
|
@ -16,11 +16,11 @@
|
||||||
│ 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/intrin/safemacros.internal.h"
|
|
||||||
#include "libc/intrin/weaken.h"
|
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/strace.internal.h"
|
#include "libc/calls/strace.internal.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/safemacros.internal.h"
|
||||||
|
#include "libc/intrin/weaken.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/x/x.h"
|
#include "libc/x/x.h"
|
||||||
|
@ -44,7 +44,12 @@ static int MakeDirs(const char *path, unsigned mode, int e) {
|
||||||
free(dir);
|
free(dir);
|
||||||
if (rc == -1) return -1;
|
if (rc == -1) return -1;
|
||||||
errno = e;
|
errno = e;
|
||||||
return mkdir(path, mode);
|
if (!mkdir(path, mode) || errno == EEXIST) {
|
||||||
|
errno = e;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,7 +60,7 @@ static int MakeDirs(const char *path, unsigned mode, int e) {
|
||||||
* @param path is a UTF-8 string, preferably relative w/ forward slashes
|
* @param path is a UTF-8 string, preferably relative w/ forward slashes
|
||||||
* @param mode can be, for example, 0755
|
* @param mode can be, for example, 0755
|
||||||
* @return 0 on success or -1 w/ errno
|
* @return 0 on success or -1 w/ errno
|
||||||
* @see mkdir()
|
* @threadsafe
|
||||||
*/
|
*/
|
||||||
int makedirs(const char *path, unsigned mode) {
|
int makedirs(const char *path, unsigned mode) {
|
||||||
return MakeDirs(path, mode, errno);
|
return MakeDirs(path, mode, errno);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/fmt/conv.h"
|
#include "libc/fmt/conv.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/gc.internal.h"
|
#include "libc/runtime/gc.internal.h"
|
||||||
#include "libc/x/x.h"
|
#include "libc/x/x.h"
|
||||||
|
|
||||||
|
@ -24,5 +25,9 @@
|
||||||
* Returns base portion of path.
|
* Returns base portion of path.
|
||||||
*/
|
*/
|
||||||
char *xbasename(const char *path) {
|
char *xbasename(const char *path) {
|
||||||
return xstrdup(basename(gc(xstrdup(path))));
|
char *base;
|
||||||
|
path = xstrdup(path);
|
||||||
|
base = xstrdup(basename(path));
|
||||||
|
free(path);
|
||||||
|
return base;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,16 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/fmt/conv.h"
|
#include "libc/fmt/conv.h"
|
||||||
#include "libc/runtime/gc.internal.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/x/x.h"
|
#include "libc/x/x.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns directory portion of path.
|
* Returns directory portion of path.
|
||||||
*/
|
*/
|
||||||
char *xdirname(const char *path) {
|
char *xdirname(const char *path) {
|
||||||
return xstrdup(dirname(gc(xstrdup(path))));
|
char *dirp;
|
||||||
|
path = xstrdup(path);
|
||||||
|
dirp = xstrdup(dirname(path));
|
||||||
|
free(path);
|
||||||
|
return dirp;
|
||||||
}
|
}
|
||||||
|
|
45
test/libc/x/makedirs_test.c
Normal file
45
test/libc/x/makedirs_test.c
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*-*- 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 2022 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/intrin/pthread.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
|
#include "libc/runtime/gc.internal.h"
|
||||||
|
#include "libc/testlib/testlib.h"
|
||||||
|
#include "libc/thread/spawn.h"
|
||||||
|
#include "libc/x/x.h"
|
||||||
|
|
||||||
|
#define DIR \
|
||||||
|
"a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/A/B/C/D/E/F/G/H/I/J/K/" \
|
||||||
|
"L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z"
|
||||||
|
|
||||||
|
pthread_barrier_t barrier;
|
||||||
|
char testlib_enable_tmp_setup_teardown;
|
||||||
|
|
||||||
|
int Worker(void *arg, int tid) {
|
||||||
|
pthread_barrier_wait(&barrier);
|
||||||
|
ASSERT_EQ(0, makedirs(DIR, 0755));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(makedirs, test) {
|
||||||
|
int i, n = 8;
|
||||||
|
struct spawn *t = gc(malloc(sizeof(struct spawn) * n));
|
||||||
|
ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, n));
|
||||||
|
for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i));
|
||||||
|
for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i));
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ TEST_LIBC_X_DIRECTDEPS = \
|
||||||
LIBC_SOCK \
|
LIBC_SOCK \
|
||||||
LIBC_STUBS \
|
LIBC_STUBS \
|
||||||
LIBC_SYSV \
|
LIBC_SYSV \
|
||||||
|
LIBC_THREAD \
|
||||||
LIBC_TESTLIB \
|
LIBC_TESTLIB \
|
||||||
LIBC_X \
|
LIBC_X \
|
||||||
THIRD_PARTY_GDTOA
|
THIRD_PARTY_GDTOA
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include "libc/runtime/sysconf.h"
|
#include "libc/runtime/sysconf.h"
|
||||||
#include "libc/stdio/append.internal.h"
|
#include "libc/stdio/append.internal.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
|
#include "libc/str/errfun.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/auxv.h"
|
#include "libc/sysv/consts/auxv.h"
|
||||||
#include "libc/sysv/consts/clock.h"
|
#include "libc/sysv/consts/clock.h"
|
||||||
|
@ -576,20 +577,37 @@ char *AddShellQuotes(const char *s) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MakeDirs(const char *path, int mode) {
|
||||||
|
if (makedirs(path, mode)) {
|
||||||
|
kprintf("error: makedirs(%#s) failed\n", path);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int Launch(void) {
|
int Launch(void) {
|
||||||
size_t got;
|
size_t got;
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
int ws, pid;
|
int ws, pid;
|
||||||
uint64_t us;
|
uint64_t us;
|
||||||
gotchld = 0;
|
gotchld = 0;
|
||||||
if (pipe2(pipefds, O_CLOEXEC) == -1) exit(errno);
|
|
||||||
|
if (pipe2(pipefds, O_CLOEXEC) == -1) {
|
||||||
|
kprintf("pipe2 failed: %s\n", strerrno(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||||
if (timeout > 0) {
|
if (timeout > 0) {
|
||||||
timer.it_value.tv_sec = timeout;
|
timer.it_value.tv_sec = timeout;
|
||||||
timer.it_interval.tv_sec = timeout;
|
timer.it_interval.tv_sec = timeout;
|
||||||
setitimer(ITIMER_REAL, &timer, 0);
|
setitimer(ITIMER_REAL, &timer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pid = vfork();
|
pid = vfork();
|
||||||
|
if (pid == -1) {
|
||||||
|
kprintf("vfork failed: %s\n", strerrno(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
int fd;
|
int fd;
|
||||||
|
@ -611,9 +629,11 @@ int Launch(void) {
|
||||||
dup2(pipefds[1], 2);
|
dup2(pipefds[1], 2);
|
||||||
sigprocmask(SIG_SETMASK, &savemask, 0);
|
sigprocmask(SIG_SETMASK, &savemask, 0);
|
||||||
execve(cmd, args.p, env.p);
|
execve(cmd, args.p, env.p);
|
||||||
|
kprintf("execve(%#s) failed: %s\n", cmd, strerrno(errno));
|
||||||
_Exit(127);
|
_Exit(127);
|
||||||
}
|
}
|
||||||
close(pipefds[1]);
|
close(pipefds[1]);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (gotchld) {
|
if (gotchld) {
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
@ -1139,7 +1159,7 @@ int main(int argc, char *argv[]) {
|
||||||
if (outpath) {
|
if (outpath) {
|
||||||
outdir = xdirname(outpath);
|
outdir = xdirname(outpath);
|
||||||
if (!isdirectory(outdir)) {
|
if (!isdirectory(outdir)) {
|
||||||
makedirs(outdir, 0755);
|
MakeDirs(outdir, 0755);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1190,7 +1210,7 @@ int main(int argc, char *argv[]) {
|
||||||
if (WIFEXITED(ws)) {
|
if (WIFEXITED(ws)) {
|
||||||
if (!(exitcode = WEXITSTATUS(ws)) || exitcode == 254) {
|
if (!(exitcode = WEXITSTATUS(ws)) || exitcode == 254) {
|
||||||
if (touchtarget && target) {
|
if (touchtarget && target) {
|
||||||
makedirs(xdirname(target), 0755);
|
MakeDirs(xdirname(target), 0755);
|
||||||
if (touch(target, 0644)) {
|
if (touch(target, 0644)) {
|
||||||
exitcode = 90;
|
exitcode = 90;
|
||||||
appends(&output, "\nfailed to touch output file\n");
|
appends(&output, "\nfailed to touch output file\n");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue