[Windows] Implement FileExists, ReadFromFile, and FindPathToBinary
authorReid Kleckner <rnk@google.com>
Mon, 10 Aug 2015 23:40:27 +0000 (23:40 +0000)
committerReid Kleckner <rnk@google.com>
Mon, 10 Aug 2015 23:40:27 +0000 (23:40 +0000)
Summary: These are needed to talk to llvm-symbolizer on Windows.

Reviewers: samsonov

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D11920

llvm-svn: 244533

compiler-rt/lib/sanitizer_common/sanitizer_common.cc
compiler-rt/lib/sanitizer_common/sanitizer_posix.cc
compiler-rt/lib/sanitizer_common/sanitizer_win.cc
compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cc
compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc

index a04863b..2307d31 100644 (file)
@@ -344,6 +344,32 @@ bool TemplateMatch(const char *templ, const char *str) {
   return true;
 }
 
+static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';
+
+char *FindPathToBinary(const char *name) {
+  const char *path = GetEnv("PATH");
+  if (!path)
+    return 0;
+  uptr name_len = internal_strlen(name);
+  InternalScopedBuffer<char> buffer(kMaxPathLength);
+  const char *beg = path;
+  while (true) {
+    const char *end = internal_strchrnul(beg, kPathSeparator);
+    uptr prefix_len = end - beg;
+    if (prefix_len + name_len + 2 <= kMaxPathLength) {
+      internal_memcpy(buffer.data(), beg, prefix_len);
+      buffer[prefix_len] = '/';
+      internal_memcpy(&buffer[prefix_len + 1], name, name_len);
+      buffer[prefix_len + 1 + name_len] = '\0';
+      if (FileExists(buffer.data()))
+        return internal_strdup(buffer.data());
+    }
+    if (*end == '\0') break;
+    beg = end + 1;
+  }
+  return nullptr;
+}
+
 static char binary_name_cache_str[kMaxPathLength];
 static char process_name_cache_str[kMaxPathLength];
 
index abf6738..b1555f3 100644 (file)
@@ -296,30 +296,6 @@ const char *GetPwd() {
   return GetEnv("PWD");
 }
 
-char *FindPathToBinary(const char *name) {
-  const char *path = GetEnv("PATH");
-  if (!path)
-    return 0;
-  uptr name_len = internal_strlen(name);
-  InternalScopedBuffer<char> buffer(kMaxPathLength);
-  const char *beg = path;
-  while (true) {
-    const char *end = internal_strchrnul(beg, ':');
-    uptr prefix_len = end - beg;
-    if (prefix_len + name_len + 2 <= kMaxPathLength) {
-      internal_memcpy(buffer.data(), beg, prefix_len);
-      buffer[prefix_len] = '/';
-      internal_memcpy(&buffer[prefix_len + 1], name, name_len);
-      buffer[prefix_len + 1 + name_len] = '\0';
-      if (FileExists(buffer.data()))
-        return internal_strdup(buffer.data());
-    }
-    if (*end == '\0') break;
-    beg = end + 1;
-  }
-  return 0;
-}
-
 bool IsPathSeparator(const char c) {
   return c == '/';
 }
index 39a4259..3dbdc50 100644 (file)
@@ -51,7 +51,7 @@ uptr GetMaxVirtualAddress() {
 }
 
 bool FileExists(const char *filename) {
-  UNIMPLEMENTED();
+  return ::GetFileAttributesA(filename) != INVALID_FILE_ATTRIBUTES;
 }
 
 uptr internal_getpid() {
@@ -292,11 +292,6 @@ void SetAddressSpaceUnlimited() {
   UNIMPLEMENTED();
 }
 
-char *FindPathToBinary(const char *name) {
-  // Nothing here for now.
-  return 0;
-}
-
 bool IsPathSeparator(const char c) {
   return c == '\\' || c == '/';
 }
@@ -515,21 +510,32 @@ bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
                  error_t *error_p) {
   CHECK(fd != kInvalidFd);
 
-  if (fd == kStdoutFd) {
-    fd = GetStdHandle(STD_OUTPUT_HANDLE);
-    if (fd == 0) fd = kInvalidFd;
-  } else if (fd == kStderrFd) {
-    fd = GetStdHandle(STD_ERROR_HANDLE);
-    if (fd == 0) fd = kInvalidFd;
+  // Handle null optional parameters.
+  error_t dummy_error;
+  error_p = error_p ? error_p : &dummy_error;
+  uptr dummy_bytes_written;
+  bytes_written = bytes_written ? bytes_written : &dummy_bytes_written;
+
+  // Initialize output parameters in case we fail.
+  *error_p = 0;
+  *bytes_written = 0;
+
+  // Map the conventional Unix fds 1 and 2 to Windows handles. They might be
+  // closed, in which case this will fail.
+  if (fd == kStdoutFd || fd == kStderrFd) {
+    fd = GetStdHandle(fd == kStdoutFd ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
+    if (fd == 0) {
+      *error_p = ERROR_INVALID_HANDLE;
+      return false;
+    }
   }
 
-  DWORD internal_bytes_written;
-  if (fd == kInvalidFd ||
-      WriteFile(fd, buff, buff_size, &internal_bytes_written, 0)) {
-    if (error_p) *error_p = GetLastError();
+  DWORD bytes_written_32;
+  if (!WriteFile(fd, buff, buff_size, &bytes_written_32, 0)) {
+    *error_p = GetLastError();
     return false;
   } else {
-    if (bytes_written) *bytes_written = internal_bytes_written;
+    *bytes_written = bytes_written_32;
     return true;
   }
 }
index e08a38c..c1bb797 100644 (file)
@@ -188,6 +188,15 @@ TEST(SanitizerCommon, FindPathToBinary) {
   InternalFree(true_path);
   EXPECT_EQ(0, FindPathToBinary("unexisting_binary.ergjeorj"));
 }
+#elif SANITIZER_WINDOWS
+TEST(SanitizerCommon, FindPathToBinary) {
+  // ntdll.dll should be on PATH in all supported test environments on all
+  // supported Windows versions.
+  char *ntdll_path = FindPathToBinary("ntdll.dll");
+  EXPECT_NE((char*)0, internal_strstr(ntdll_path, "ntdll.dll"));
+  InternalFree(ntdll_path);
+  EXPECT_EQ(0, FindPathToBinary("unexisting_binary.ergjeorj"));
+}
 #endif
 
 TEST(SanitizerCommon, StripPathPrefix) {
index 4ed6a69..c0230ec 100644 (file)
@@ -14,6 +14,9 @@
 #include "sanitizer_common/sanitizer_platform.h"
 #include "gtest/gtest.h"
 
+#if SANITIZER_WINDOWS
+#include <windows.h>
+#endif
 #if SANITIZER_POSIX
 # include <sys/stat.h>
 # include "sanitizer_common/sanitizer_posix.h"
@@ -54,6 +57,17 @@ struct stat_and_more {
 };
 
 static void temp_file_name(char *buf, size_t bufsize, const char *prefix) {
+#if SANITIZER_WINDOWS
+  buf[0] = '\0';
+  char tmp_dir[MAX_PATH];
+  if (!::GetTempPathA(MAX_PATH, tmp_dir))
+    return;
+  // GetTempFileNameA needs a MAX_PATH buffer.
+  char tmp_path[MAX_PATH];
+  if (!::GetTempFileNameA(tmp_dir, prefix, 0, tmp_path))
+    return;
+  internal_strncpy(buf, tmp_path, bufsize);
+#else
   const char *tmpdir = "/tmp";
 #if SANITIZER_ANDROID
   // I don't know a way to query temp directory location on Android without
@@ -64,10 +78,9 @@ static void temp_file_name(char *buf, size_t bufsize, const char *prefix) {
 #endif
   u32 uid = GetUid();
   internal_snprintf(buf, bufsize, "%s/%s%d", tmpdir, prefix, uid);
+#endif
 }
 
-// FIXME: File manipulations are not yet supported on Windows
-#if SANITIZER_POSIX
 TEST(SanitizerCommon, FileOps) {
   const char *str1 = "qwerty";
   uptr len1 = internal_strlen(str1);
@@ -78,12 +91,20 @@ TEST(SanitizerCommon, FileOps) {
   temp_file_name(tmpfile, sizeof(tmpfile), "sanitizer_common.fileops.tmp.");
   fd_t fd = OpenFile(tmpfile, WrOnly);
   ASSERT_NE(fd, kInvalidFd);
-  EXPECT_EQ(len1, internal_write(fd, str1, len1));
-  EXPECT_EQ(len2, internal_write(fd, str2, len2));
+  uptr bytes_written = 0;
+  EXPECT_TRUE(WriteToFile(fd, str1, len1, &bytes_written));
+  EXPECT_EQ(len1, bytes_written);
+  EXPECT_TRUE(WriteToFile(fd, str2, len2, &bytes_written));
+  EXPECT_EQ(len2, bytes_written);
   CloseFile(fd);
 
+  EXPECT_TRUE(FileExists(tmpfile));
+
   fd = OpenFile(tmpfile, RdOnly);
   ASSERT_NE(fd, kInvalidFd);
+
+#if SANITIZER_POSIX
+  // The stat wrappers are posix-only.
   uptr fsize = internal_filesize(fd);
   EXPECT_EQ(len1 + len2, fsize);
 
@@ -101,18 +122,28 @@ TEST(SanitizerCommon, FileOps) {
   EXPECT_EQ(0xAB, sam.z);
   EXPECT_NE(0xAB, sam.st.st_size);
   EXPECT_NE(0, sam.st.st_size);
+#endif
 
   char buf[64] = {};
-  EXPECT_EQ(len1, internal_read(fd, buf, len1));
+  uptr bytes_read = 0;
+  EXPECT_TRUE(ReadFromFile(fd, buf, len1, &bytes_read));
+  EXPECT_EQ(len1, bytes_read);
   EXPECT_EQ(0, internal_memcmp(buf, str1, len1));
   EXPECT_EQ((char)0, buf[len1 + 1]);
   internal_memset(buf, 0, len1);
-  EXPECT_EQ(len2, internal_read(fd, buf, len2));
+  EXPECT_TRUE(ReadFromFile(fd, buf, len2, &bytes_read));
+  EXPECT_EQ(len2, bytes_read);
   EXPECT_EQ(0, internal_memcmp(buf, str2, len2));
   CloseFile(fd);
+
+#if SANITIZER_WINDOWS
+  // No sanitizer needs to delete a file on Windows yet. If we ever do, we can
+  // add a portable wrapper and test it from here.
+  ::DeleteFileA(&tmpfile[0]);
+#else
   internal_unlink(tmpfile);
-}
 #endif
+}
 
 TEST(SanitizerCommon, InternalStrFunctions) {
   const char *haystack = "haystack";