--- ./src/ld/Snapshot.cpp.orig 2020-01-26 11:39:26.000000000 -0800 +++ ./src/ld/Snapshot.cpp 2020-01-26 11:40:12.000000000 -0800 @@ -23,6 +23,166 @@ #include "compile_stubs.h" +int +_mkpath_np(const char *path, mode_t omode, const char ** firstdir) +{ + char *apath = NULL; + unsigned int depth = 0; + mode_t chmod_mode = 0; + int retval = 0; + int old_errno = errno; + struct stat sbuf; + + /* Try the trivial case first. */ + if (0 == mkdir(path, omode)) { + if (firstdir) { + *firstdir = strdup(path); + } + goto mkpath_exit; + } + + /* Anything other than an ENOENT, EEXIST, or EISDIR indicates an + * error that we need to send back to the caller. ENOENT indicates + * that we need to try a lower level. + */ + switch (errno) { + case ENOENT: + break; + case EEXIST: + if (stat(path, &sbuf) == 0) { + if (S_ISDIR(sbuf.st_mode)) { + retval = EEXIST; + } else { + retval = ENOTDIR; + } + } else { + retval = EIO; + } + goto mkpath_exit; + case EISDIR: /* */ + retval = EEXIST; + goto mkpath_exit; + default: + retval = errno; + goto mkpath_exit; + } + + apath = strdup(path); + if (apath == NULL) { + retval = ENOMEM; + goto mkpath_exit; + } + + while (1) { + /* Increase our depth and try making that directory */ + char *s = strrchr(apath, '/'); + if (!s) { + /* We should never hit this under normal circumstances, + * but it can occur due to really unfortunate timing + */ + retval = ENOENT; + goto mkpath_exit; + } + *s = '\0'; + depth++; + + if (0 == mkdir(apath, S_IRWXU | S_IRWXG | S_IRWXO)) { + /* Found our starting point */ + + /* POSIX 1003.2: + * For each dir operand that does not name an existing + * directory, effects equivalent to those cased by the + * following command shall occcur: + * + * mkdir -p -m $(umask -S),u+wx $(dirname dir) && + * mkdir [-m mode] dir + */ + + struct stat dirstat; + if (-1 == stat(apath, &dirstat)) { + /* Really unfortunate timing ... */ + retval = ENOENT; + goto mkpath_exit; + } + + if ((dirstat.st_mode & (S_IWUSR | S_IXUSR)) != (S_IWUSR | S_IXUSR)) { + chmod_mode = dirstat.st_mode | S_IWUSR | S_IXUSR; + if (-1 == chmod(apath, chmod_mode)) { + /* Really unfortunate timing ... */ + retval = ENOENT; + goto mkpath_exit; + } + } + + if (firstdir) { + *firstdir = strdup(apath); + } + break; + } else if (errno == EEXIST) { + /* Some other process won the race in creating this directory + * before we did. We will use this as our starting point. + * See: + */ + if (stat(apath, &sbuf) == 0 && + S_ISDIR(sbuf.st_mode)) { + + if (firstdir) { + *firstdir = strdup(apath); + } + break; + } + + retval = ENOTDIR; + goto mkpath_exit; + } else if (errno != ENOENT) { + retval = errno; + goto mkpath_exit; + } + } + + while (depth > 1) { + /* Decrease our depth and make that directory */ + char *s = strrchr(apath, '\0'); + *s = '/'; + depth--; + + if (-1 == mkdir(apath, S_IRWXU | S_IRWXG | S_IRWXO)) { + /* This handles "." and ".." added to the new section of path */ + if (errno == EEXIST) + continue; + retval = errno; + goto mkpath_exit; + } + + if (chmod_mode) { + if (-1 == chmod(apath, chmod_mode)) { + /* Really unfortunate timing ... */ + retval = ENOENT; + goto mkpath_exit; + } + } + } + + if (-1 == mkdir(path, omode)) { + retval = errno; + if (errno == EEXIST && + stat(path, &sbuf) == 0 && + !S_ISDIR(sbuf.st_mode)) { + retval = ENOTDIR; + } + } + +mkpath_exit: + free(apath); + + errno = old_errno; + return retval; +} + +int mkpath_np(const char *path, mode_t omode) { + return _mkpath_np(path, omode, NULL); +} + //#define STORE_PID_IN_SNAPSHOT 1 // Well known snapshot file/directory names. These appear in the root of the snapshot.