Make android::base::Dirname() thread-safe.
See 91a10d912827b818d0c1931ede3a2afaa93b18cd where we did this for
Basename(). We don't know that Dirname() has caused similar problems,
but there's no reason it couldn't...
Bug: http://b/231951809
Test: treehugger
Change-Id: I64bb362a3652654066101511a7a500c8223d62f7
diff --git a/file.cpp b/file.cpp
index a580dcc..d0cec7d 100644
--- a/file.cpp
+++ b/file.cpp
@@ -23,6 +23,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -553,18 +554,19 @@
}
#endif
+#if defined(_WIN32)
std::string Dirname(const std::string& path) {
+ // TODO: how much of this is actually necessary for mingw?
+
// Copy path because dirname may modify the string passed in.
std::string result(path);
-#if !defined(__BIONIC__)
// Use lock because dirname() may write to a process global and return a
// pointer to that. Note that this locking strategy only works if all other
// callers to dirname in the process also grab this same lock, but its
// better than nothing. Bionic's dirname returns a thread-local buffer.
static std::mutex& dirname_lock = *new std::mutex();
std::lock_guard<std::mutex> lock(dirname_lock);
-#endif
// Note that if std::string uses copy-on-write strings, &str[0] will cause
// the copy to be made, so there is no chance of us accidentally writing to
@@ -577,6 +579,72 @@
return result;
}
+#else
+// Copied from bionic so that Dirname() below can be portable and thread-safe.
+static int __dirname_r(const char* path, char* buffer, size_t buffer_size) {
+ const char* endp = nullptr;
+ int len;
+ int result;
+
+ // Empty or NULL string gets treated as ".".
+ if (path == nullptr || *path == '\0') {
+ path = ".";
+ len = 1;
+ goto Exit;
+ }
+
+ // Strip trailing slashes.
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/') {
+ endp--;
+ }
+
+ // Find the start of the dir.
+ while (endp > path && *endp != '/') {
+ endp--;
+ }
+
+ // Either the dir is "/" or there are no slashes.
+ if (endp == path) {
+ path = (*endp == '/') ? "/" : ".";
+ len = 1;
+ goto Exit;
+ }
+
+ do {
+ endp--;
+ } while (endp > path && *endp == '/');
+
+ len = endp - path + 1;
+
+ Exit:
+ result = len;
+ if (len + 1 > MAXPATHLEN) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ if (buffer == nullptr) {
+ return result;
+ }
+
+ if (len > static_cast<int>(buffer_size) - 1) {
+ len = buffer_size - 1;
+ result = -1;
+ errno = ERANGE;
+ }
+
+ if (len >= 0) {
+ memcpy(buffer, path, len);
+ buffer[len] = 0;
+ }
+ return result;
+}
+std::string Dirname(const std::string& path) {
+ char buf[PATH_MAX];
+ __dirname_r(path.c_str(), buf, sizeof(buf));
+ return buf;
+}
+#endif
} // namespace base
} // namespace android