Fix TODO in recursive directory creation -- MOS_MIGRATED_REVID=90517845
diff --git a/src/main/cpp/blaze_util.cc b/src/main/cpp/blaze_util.cc index 70e52ff..782c87a 100644 --- a/src/main/cpp/blaze_util.cc +++ b/src/main/cpp/blaze_util.cc
@@ -29,6 +29,7 @@ #include <unistd.h> #include <sstream> +#include "util/file.h" #include "util/numbers.h" #include "util/strings.h" @@ -89,23 +90,62 @@ return cwdbuf + separator + path; } -// mkdir -p path. Returns -1 on failure, sets errno. -int MakeDirectories(string path, int mode) { - path.push_back('\0'); - char *buf = &path[0]; - for (char *slash = strchr(buf + 1, '/'); slash != NULL; - slash = strchr(slash + 1, '/')) { - *slash = '\0'; - if (mkdir(buf, mode) == -1 && errno != EEXIST) { - return -1; - } - *slash = '/'; - } - // TODO(bazel-team): EEXIST does not prove that it's a directory! - if (mkdir(buf, mode) == -1 && errno != EEXIST) { +static int MakeDirectories_(string path, int mode, bool childmost) { + if (path.empty() || path == "/") { + errno = EACCES; return -1; } - return 0; + + struct stat filestat = {}; + if (stat(path.c_str(), &filestat) == 0) { + if (S_ISDIR(filestat.st_mode)) { + // Only check permissions if this is the actual directory we're trying to + // create. + if (childmost) { + // If this is a symlink, run checks on the link. (If we did lstat above + // then it would return false for ISDIR). + struct stat linkstat = {}; + if (lstat(path.c_str(), &linkstat) != 0) { + return -1; + } + if (linkstat.st_uid != geteuid()) { + // The directory isn't owned by me. + errno = EACCES; + return -1; + } + if ((filestat.st_mode & 0777) != mode + && chmod(path.c_str(), mode) == -1) { + // errno set by chmod. + return -1; + } + } + return 0; + } else { + errno = ENOTDIR; + return -1; + } + } + + if (errno == ENOENT) { + // Path does not exist, attempt to create its parents, then it. + string parent = blaze_util::Dirname(path); + if (MakeDirectories_(parent, mode, false) == 0 + && mkdir(path.c_str(), mode) == 0) { + return 0; + } + } + + // errno set by stat. + return -1; +} + +// mkdir -p path. Returns 0 if the path was created or already exists and could +// be chmod-ed to exactly the given permissions. If final part of the path is a +// symlink, this ensures that the destination of the symlink has the desired +// permissions. It also checks that the directory or symlink is owned by us. +// On failure, this returns -1 and sets errno. +int MakeDirectories(string path, int mode) { + return MakeDirectories_(path, mode, true); } // Replaces 'contents' with contents of 'fd' file descriptor.