mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +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 │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/x/x.h"
|
||||
|
@ -44,7 +44,12 @@ static int MakeDirs(const char *path, unsigned mode, int e) {
|
|||
free(dir);
|
||||
if (rc == -1) return -1;
|
||||
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 mode can be, for example, 0755
|
||||
* @return 0 on success or -1 w/ errno
|
||||
* @see mkdir()
|
||||
* @threadsafe
|
||||
*/
|
||||
int makedirs(const char *path, unsigned mode) {
|
||||
return MakeDirs(path, mode, errno);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
|
@ -24,5 +25,9 @@
|
|||
* Returns base portion of 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. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
/**
|
||||
* Returns directory portion of 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_STUBS \
|
||||
LIBC_SYSV \
|
||||
LIBC_THREAD \
|
||||
LIBC_TESTLIB \
|
||||
LIBC_X \
|
||||
THIRD_PARTY_GDTOA
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/stdio/append.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/errfun.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
|
@ -576,20 +577,37 @@ char *AddShellQuotes(const char *s) {
|
|||
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) {
|
||||
size_t got;
|
||||
ssize_t rc;
|
||||
int ws, pid;
|
||||
uint64_t us;
|
||||
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);
|
||||
if (timeout > 0) {
|
||||
timer.it_value.tv_sec = timeout;
|
||||
timer.it_interval.tv_sec = timeout;
|
||||
setitimer(ITIMER_REAL, &timer, 0);
|
||||
}
|
||||
|
||||
pid = vfork();
|
||||
if (pid == -1) {
|
||||
kprintf("vfork failed: %s\n", strerrno(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
int fd;
|
||||
|
@ -611,9 +629,11 @@ int Launch(void) {
|
|||
dup2(pipefds[1], 2);
|
||||
sigprocmask(SIG_SETMASK, &savemask, 0);
|
||||
execve(cmd, args.p, env.p);
|
||||
kprintf("execve(%#s) failed: %s\n", cmd, strerrno(errno));
|
||||
_Exit(127);
|
||||
}
|
||||
close(pipefds[1]);
|
||||
|
||||
for (;;) {
|
||||
if (gotchld) {
|
||||
rc = 0;
|
||||
|
@ -1139,7 +1159,7 @@ int main(int argc, char *argv[]) {
|
|||
if (outpath) {
|
||||
outdir = xdirname(outpath);
|
||||
if (!isdirectory(outdir)) {
|
||||
makedirs(outdir, 0755);
|
||||
MakeDirs(outdir, 0755);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1190,7 +1210,7 @@ int main(int argc, char *argv[]) {
|
|||
if (WIFEXITED(ws)) {
|
||||
if (!(exitcode = WEXITSTATUS(ws)) || exitcode == 254) {
|
||||
if (touchtarget && target) {
|
||||
makedirs(xdirname(target), 0755);
|
||||
MakeDirs(xdirname(target), 0755);
|
||||
if (touch(target, 0644)) {
|
||||
exitcode = 90;
|
||||
appends(&output, "\nfailed to touch output file\n");
|
||||
|
|
Loading…
Reference in a new issue