]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Implement Util::real_path, replacing legacy x_realpath
authorJoel Rosdahl <joel@rosdahl.net>
Tue, 11 Feb 2020 20:34:43 +0000 (21:34 +0100)
committerJoel Rosdahl <joel@rosdahl.net>
Sat, 15 Feb 2020 15:39:48 +0000 (16:39 +0100)
Makefile.in
src/Util.cpp
src/Util.hpp
src/ccache.cpp
src/execute.cpp
src/legacy_util.cpp
src/legacy_util.hpp
src/win32compat.cpp [new file with mode: 0644]
src/win32compat.hpp [new file with mode: 0644]
unittest/test_argument_processing.cpp

index cf2c81fbe204709a391d265c8872f5cf72336686..ebd29d833e4d968e8192388d8df06784acd58ea9 100644 (file)
@@ -65,7 +65,8 @@ non_third_party_sources = \
     src/logging.cpp \
     src/manifest.cpp \
     src/result.cpp \
-    src/stats.cpp
+    src/stats.cpp \
+    src/win32compat.cpp
 generated_sources = \
     src/version.cpp
 third_party_sources = \
index 591ef074deea06032a0af009af0327c74922d500..59024626c526f8f3a964242e7a0e17877b260c98 100644 (file)
 #include <algorithm>
 #include <fstream>
 
+#ifdef _WIN32
+#  include "win32compat.hpp"
+#endif
+
 using nonstd::string_view;
 
 namespace {
@@ -70,6 +74,21 @@ get_cache_files_internal(const std::string& dir,
   }
 }
 
+size_t
+path_max(const char* path)
+{
+#ifdef PATH_MAX
+  (void)path;
+  return PATH_MAX;
+#elif defined(MAXPATHLEN)
+  (void)path;
+  return MAXPATHLEN;
+#elif defined(_PC_PATH_MAX)
+  long maxlen = pathconf(path, _PC_PATH_MAX);
+  return maxlen >= 4096 ? maxlen : 4096;
+#endif
+}
+
 } // namespace
 
 namespace Util {
@@ -262,6 +281,56 @@ read_file(const std::string& path)
                      std::istreambuf_iterator<char>());
 }
 
+std::string
+real_path(const std::string& path, bool return_empty_on_error)
+{
+  const char* c_path = path.c_str();
+  size_t buffer_size = path_max(c_path);
+  std::unique_ptr<char[]> managed_buffer(new char[buffer_size]);
+  char* buffer = managed_buffer.get();
+  char* resolved = nullptr;
+
+#if HAVE_REALPATH
+  resolved = realpath(c_path, buffer);
+#elif defined(_WIN32)
+  if (c_path[0] == '/') {
+    c_path++; // Skip leading slash.
+  }
+  HANDLE path_handle = CreateFile(c_path,
+                                  GENERIC_READ,
+                                  FILE_SHARE_READ,
+                                  NULL,
+                                  OPEN_EXISTING,
+                                  FILE_ATTRIBUTE_NORMAL,
+                                  NULL);
+  if (INVALID_HANDLE_VALUE != path_handle) {
+#  ifdef HAVE_GETFINALPATHNAMEBYHANDLEW
+    GetFinalPathNameByHandle(
+      path_handle, buffer, buffer_size, FILE_NAME_NORMALIZED);
+#  else
+    GetFileNameFromHandle(path_handle, buffer, buffer_size);
+#  endif
+    CloseHandle(path_handle);
+    resolved = buffer + 4; // Strip \\?\ from the file name.
+  } else {
+    snprintf(buffer, buffer_size, "%s", c_path);
+    resolved = buffer;
+  }
+#else
+  // Yes, there are such systems. This replacement relies on the fact that when
+  // we call x_realpath we only care about symlinks.
+  {
+    ssize_t len = readlink(c_path, buffer, buffer_size - 1);
+    if (len != -1) {
+      buffer[len] = 0;
+      resolved = buffer;
+    }
+  }
+#endif
+
+  return resolved ? resolved : (return_empty_on_error ? "" : path);
+}
+
 string_view
 remove_extension(string_view path)
 {
index 19237c797de62f80f151ea9968fdfdaf7ff65f01..1d70aadcffc0d4c70b49203f7bf61a2f8e117f4f 100644 (file)
@@ -189,6 +189,12 @@ int parse_int(const std::string& value);
 // Throws Error on error.
 std::string read_file(const std::string& path);
 
+// Return a canonicalized absolute path of `path`. On error (e.g. if the `path`
+// doesn't exist) the empty string is returned if return_empty_on_error is true,
+// otherwise `path` unmodified.
+std::string real_path(const std::string& path,
+                      bool return_empty_on_error = false);
+
 // Return a view into `path` containing the given path without the filename
 // extension as determined by `get_extension()`.
 nonstd::string_view remove_extension(nonstd::string_view path);
index 528ccbec5c3274034f7bdd59c783e73209e50d3d..d83c05e2d972b0d365375c4565bb0dbb7160193c 100644 (file)
@@ -421,7 +421,7 @@ get_current_working_dir(void)
   if (!current_working_dir) {
     char* cwd = get_cwd();
     if (cwd) {
-      current_working_dir = x_realpath(cwd);
+      current_working_dir = x_strdup(Util::real_path(cwd).c_str());
       free(cwd);
     }
     if (!current_working_dir) {
@@ -647,11 +647,10 @@ make_relative_path(const char* path)
 
   char* dir = nullptr;
   char* path_suffix = nullptr;
-  char* canon_path = nullptr;
   char* relpath = nullptr;
 
-  // x_realpath only works for existing paths, so if path doesn't exist, try
-  // x_dirname(path) and assemble the path afterwards. We only bother to try
+  // Util::real_path only works for existing paths, so if path doesn't exist,
+  // try x_dirname(path) and assemble the path afterwards. We only bother to try
   // canonicalizing one of these two paths since a compiler path argument
   // typically only makes sense if path or x_dirname(path) exists.
 
@@ -676,9 +675,9 @@ make_relative_path(const char* path)
 
   std::string result;
 
-  canon_path = x_realpath(path);
-  if (canon_path) {
-    relpath = get_relative_path(get_current_working_dir(), canon_path);
+  std::string canon_path = Util::real_path(path, true);
+  if (!canon_path.empty()) {
+    relpath = get_relative_path(get_current_working_dir(), canon_path.c_str());
     if (path_suffix) {
       result = fmt::format("{}/{}", relpath, path_suffix);
     } else {
@@ -694,7 +693,6 @@ make_relative_path(const char* path)
 #endif
   free(dir);
   free(path_suffix);
-  free(canon_path);
   free(relpath);
 
   return result;
@@ -1677,23 +1675,19 @@ hash_common_info(Context& ctx,
 
   // Possibly hash the coverage data file path.
   if (ctx.args_info.generating_coverage && ctx.args_info.profile_arcs) {
-    char* dir = x_dirname(ctx.args_info.output_obj.c_str());
+    std::string dir;
     if (!ctx.args_info.profile_dir.empty()) {
-      dir = x_strdup(ctx.args_info.profile_dir.c_str());
+      dir = ctx.args_info.profile_dir;
     } else {
-      char* real_dir = x_realpath(dir);
-      free(dir);
-      dir = real_dir;
-    }
-    if (dir) {
-      string_view base_name = Util::base_name(ctx.args_info.output_obj);
-      string_view p = Util::remove_extension(base_name);
-      std::string gcda_path = fmt::format("{}/{}.gcda", dir, p);
-      cc_log("Hashing coverage path %s", gcda_path.c_str());
-      hash_delimiter(hash, "gcda");
-      hash_string(hash, gcda_path);
-      free(dir);
+      dir =
+        Util::real_path(std::string(Util::dir_name(ctx.args_info.output_obj)));
     }
+    string_view stem =
+      Util::remove_extension(Util::base_name(ctx.args_info.output_obj));
+    std::string gcda_path = fmt::format("{}/{}.gcda", dir, stem);
+    cc_log("Hashing coverage path %s", gcda_path.c_str());
+    hash_delimiter(hash, "gcda");
+    hash_string(hash, gcda_path);
   }
 
   // Possibly hash the sanitize blacklist file path.
@@ -2801,19 +2795,14 @@ cc_process_args(ArgsInfo& args_info,
       const char* arg_profile_dir = strchr(argv[i], '=');
       if (arg_profile_dir) {
         // Convert to absolute path.
-        char* dir = x_realpath(arg_profile_dir + 1);
-        if (!dir) {
-          // Directory doesn't exist.
-          dir = x_strdup(arg_profile_dir + 1);
-        }
+        std::string dir = Util::real_path(arg_profile_dir + 1);
 
         // We can get a better hit rate by using the real path here.
         free(arg);
         char* option = x_strndup(argv[i], arg_profile_dir - argv[i]);
-        arg = format("%s=%s", option, dir);
+        arg = format("%s=%s", option, dir.c_str());
         cc_log("Rewriting %s to %s", argv[i], arg);
         free(option);
-        free(dir);
       }
 
       bool supported_profile_option = false;
index 0413efc96f27dca232d269bbc92a8261e18b87ff..3d5045e1140cf2eb13108e8504c60f2b373d2450 100644 (file)
@@ -349,21 +349,17 @@ find_executable_in_path(const char* name,
       return x_strdup(namebuf);
     }
 #else
+    assert(exclude_name);
     char* fname = format("%s/%s", tok, name);
     auto st1 = Stat::lstat(fname);
     auto st2 = Stat::stat(fname);
     // Look for a normal executable file.
     if (st1 && st2 && st2.is_regular() && access(fname, X_OK) == 0) {
       if (st1.is_symlink()) {
-        char* buf = x_realpath(fname);
-        if (buf) {
-          string_view p = Util::base_name(buf);
-          if (p == exclude_name) {
-            // It's a link to "ccache"!
-            free(buf);
-            continue;
-          }
-          free(buf);
+        std::string real_path = Util::real_path(fname, true);
+        if (Util::base_name(real_path) == exclude_name) {
+          // It's a link to "ccache"!
+          continue;
         }
       }
 
index 728aeb0da805355f7b24bbaabce8cf048014b24b..80832c20f7d8054a53028989f7acad4de5c85f17 100644 (file)
 #  endif
 #endif
 
-#ifdef _WIN32
-#  include <psapi.h>
-#  include <sys/locking.h>
-#  include <tchar.h>
-#endif
-
 static long
 path_max(const char* path)
 {
@@ -603,138 +597,6 @@ parse_size_with_suffix(const char* str, uint64_t* size)
   return true;
 }
 
-#if !defined(HAVE_REALPATH) && defined(_WIN32)                                 \
-  && !defined(HAVE_GETFINALPATHNAMEBYHANDLEW)
-static BOOL
-GetFileNameFromHandle(HANDLE file_handle, TCHAR* filename, WORD cch_filename)
-{
-  BOOL success = FALSE;
-
-  // Get the file size.
-  DWORD file_size_hi = 0;
-  DWORD file_size_lo = GetFileSize(file_handle, &file_size_hi);
-  if (file_size_lo == 0 && file_size_hi == 0) {
-    // Cannot map a file with a length of zero.
-    return FALSE;
-  }
-
-  // Create a file mapping object.
-  HANDLE file_map =
-    CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 1, NULL);
-  if (!file_map) {
-    return FALSE;
-  }
-
-  // Create a file mapping to get the file name.
-  void* mem = MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 1);
-  if (mem) {
-    if (GetMappedFileName(GetCurrentProcess(), mem, filename, cch_filename)) {
-      // Translate path with device name to drive letters.
-      TCHAR temp[512];
-      temp[0] = '\0';
-
-      if (GetLogicalDriveStrings(512 - 1, temp)) {
-        TCHAR name[MAX_PATH];
-        TCHAR drive[3] = TEXT(" :");
-        BOOL found = FALSE;
-        TCHAR* p = temp;
-
-        do {
-          // Copy the drive letter to the template string.
-          *drive = *p;
-
-          // Look up each device name.
-          if (QueryDosDevice(drive, name, MAX_PATH)) {
-            size_t name_len = _tcslen(name);
-            if (name_len < MAX_PATH) {
-              found = _tcsnicmp(filename, name, name_len) == 0
-                      && *(filename + name_len) == _T('\\');
-              if (found) {
-                // Reconstruct filename using temp_file and replace device path
-                // with DOS path.
-                TCHAR temp_file[MAX_PATH];
-                _sntprintf(temp_file,
-                           MAX_PATH - 1,
-                           TEXT("%s%s"),
-                           drive,
-                           filename + name_len);
-                strcpy(filename, temp_file);
-              }
-            }
-          }
-
-          // Go to the next NULL character.
-          while (*p++) {
-            // Do nothing.
-          }
-        } while (!found && *p); // End of string.
-      }
-    }
-    success = TRUE;
-    UnmapViewOfFile(mem);
-  }
-
-  CloseHandle(file_map);
-  return success;
-}
-#endif
-
-// A sane realpath() function, trying to cope with stupid path limits and a
-// broken API. Caller frees.
-char*
-x_realpath(const char* path)
-{
-  long maxlen = path_max(path);
-  char* ret = static_cast<char*>(x_malloc(maxlen));
-  char* p;
-
-#if HAVE_REALPATH
-  p = realpath(path, ret);
-#elif defined(_WIN32)
-  if (path[0] == '/') {
-    path++; // Skip leading slash.
-  }
-  HANDLE path_handle = CreateFile(path,
-                                  GENERIC_READ,
-                                  FILE_SHARE_READ,
-                                  NULL,
-                                  OPEN_EXISTING,
-                                  FILE_ATTRIBUTE_NORMAL,
-                                  NULL);
-  if (INVALID_HANDLE_VALUE != path_handle) {
-#  ifdef HAVE_GETFINALPATHNAMEBYHANDLEW
-    GetFinalPathNameByHandle(path_handle, ret, maxlen, FILE_NAME_NORMALIZED);
-#  else
-    GetFileNameFromHandle(path_handle, ret, maxlen);
-#  endif
-    CloseHandle(path_handle);
-    p = ret + 4; // Strip \\?\ from the file name.
-  } else {
-    snprintf(ret, maxlen, "%s", path);
-    p = ret;
-  }
-#else
-  // Yes, there are such systems. This replacement relies on the fact that when
-  // we call x_realpath we only care about symlinks.
-  {
-    int len = readlink(path, ret, maxlen - 1);
-    if (len == -1) {
-      free(ret);
-      return NULL;
-    }
-    ret[len] = 0;
-    p = ret;
-  }
-#endif
-  if (p) {
-    p = x_strdup(p);
-    free(ret);
-    return p;
-  }
-  free(ret);
-  return NULL;
-}
-
 // A getcwd that will returns an allocated buffer.
 char*
 gnu_getcwd(void)
index 55f8902c2a1f3c196d5f4784c793ee74e45dd808..b21a257653ac8459065d75e77097185ac18efdf1 100644 (file)
@@ -44,7 +44,6 @@ const char* get_extension(const char* path);
 char* format_human_readable_size(uint64_t size);
 char* format_parsable_size_with_suffix(uint64_t size);
 bool parse_size_with_suffix(const char* str, uint64_t* size);
-char* x_realpath(const char* path);
 char* gnu_getcwd();
 #ifndef HAVE_LOCALTIME_R
 struct tm* localtime_r(const time_t* timep, struct tm* result);
diff --git a/src/win32compat.cpp b/src/win32compat.cpp
new file mode 100644 (file)
index 0000000..7728b65
--- /dev/null
@@ -0,0 +1,102 @@
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "win32compat.hpp"
+
+#ifdef _WIN32
+
+#  include <psapi.h>
+#  include <sys/locking.h>
+#  include <tchar.h>
+
+#  if !defined(HAVE_REALPATH) && !defined(HAVE_GETFINALPATHNAMEBYHANDLEW)
+BOOL
+GetFileNameFromHandle(HANDLE file_handle, TCHAR* filename, WORD cch_filename)
+{
+  BOOL success = FALSE;
+
+  // Get the file size.
+  DWORD file_size_hi = 0;
+  DWORD file_size_lo = GetFileSize(file_handle, &file_size_hi);
+  if (file_size_lo == 0 && file_size_hi == 0) {
+    // Cannot map a file with a length of zero.
+    return FALSE;
+  }
+
+  // Create a file mapping object.
+  HANDLE file_map =
+    CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 1, NULL);
+  if (!file_map) {
+    return FALSE;
+  }
+
+  // Create a file mapping to get the file name.
+  void* mem = MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 1);
+  if (mem) {
+    if (GetMappedFileName(GetCurrentProcess(), mem, filename, cch_filename)) {
+      // Translate path with device name to drive letters.
+      TCHAR temp[512];
+      temp[0] = '\0';
+
+      if (GetLogicalDriveStrings(512 - 1, temp)) {
+        TCHAR name[MAX_PATH];
+        TCHAR drive[3] = TEXT(" :");
+        BOOL found = FALSE;
+        TCHAR* p = temp;
+
+        do {
+          // Copy the drive letter to the template string.
+          *drive = *p;
+
+          // Look up each device name.
+          if (QueryDosDevice(drive, name, MAX_PATH)) {
+            size_t name_len = _tcslen(name);
+            if (name_len < MAX_PATH) {
+              found = _tcsnicmp(filename, name, name_len) == 0
+                      && *(filename + name_len) == _T('\\');
+              if (found) {
+                // Reconstruct filename using temp_file and replace device path
+                // with DOS path.
+                TCHAR temp_file[MAX_PATH];
+                _sntprintf(temp_file,
+                           MAX_PATH - 1,
+                           TEXT("%s%s"),
+                           drive,
+                           filename + name_len);
+                strcpy(filename, temp_file);
+              }
+            }
+          }
+
+          // Go to the next NULL character.
+          while (*p++) {
+            // Do nothing.
+          }
+        } while (!found && *p); // End of string.
+      }
+    }
+    success = TRUE;
+    UnmapViewOfFile(mem);
+  }
+
+  CloseHandle(file_map);
+  return success;
+}
+#  endif
+
+#endif
diff --git a/src/win32compat.hpp b/src/win32compat.hpp
new file mode 100644 (file)
index 0000000..4d0e699
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#ifdef _WIN32
+#  include "system.hpp"
+
+BOOL
+GetFileNameFromHandle(HANDLE file_handle, TCHAR* filename, WORD cch_filename);
+
+#endif
index 6171f9bd4a85459e81206f30216ec03bee40f757..ce8d9d9b04d513b9eebd3c79cff34575862ea2e0 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "../src/Config.hpp"
 #include "../src/Context.hpp"
+#include "../src/Util.hpp"
 #include "../src/args.hpp"
 #include "../src/ccache.hpp"
 #include "../src/legacy_globals.hpp"
@@ -608,14 +609,13 @@ TEST(fprofile_flag_with_existing_dir_should_be_rewritten_to_real_path)
   struct args* act_extra = NULL;
   struct args* act_cc = NULL;
 
-  char *s, *path;
+  char* s;
 
   create_file("foo.c", "");
   mkdir("some", 0777);
   mkdir("some/dir", 0777);
-  path = x_realpath("some/dir");
-  s = format("-fprofile-generate=%s", path);
-  free(path);
+  std::string path = Util::real_path("some/dir");
+  s = format("-fprofile-generate=%s", path.c_str());
   args_add(exp_cpp, s);
   args_add(exp_cc, s);
   args_add(exp_cc, "-c");