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.