[PATCH] cygcheck: follow symbolic links

Igor Peshansky pechtcha@cs.nyu.edu
Mon Feb 13 18:44:00 GMT 2006


As promised in <http://cygwin.com/ml/cygwin/2006-02/msg00071.html>, the
attached patch teaches cygcheck to follow symlinks when looking for
executables, and also flags scripts.  Sorry, had legal delays in sending
this (as you can see, I had this working since 02/03).  Comments welcome.
	Igor

2006-02-03  Igor Peshansky  <pechtcha@cs.nyu.edu>

	* cygcheck.cc (LINK_EXTENSION): New macro.
	(check_existence): New static function.
	(find_on_path): Check for symbolic links if asked.
	(EXE_MAGIC,SHORTCUT_MAGIC,SYMLINK_COOKIE,SYMLINK_MAGIC): New
	macros.
	(is_exe,is_symlink,readlink): New static functions.
	(track_down): Only call dll_info() for executables, display
	an error for symlinks, and print magic number for others.
	(find_app_on_path): New static function.
	(cygcheck, dump_sysinfo): Call find_app_on_path() instead of
	find_on_path().

-- 
				http://cs.nyu.edu/~pechtcha/
      |\      _,,,---,,_	    pechtcha@cs.nyu.edu | igor@watson.ibm.com
ZZZzz /,`.-'`'    -.  ;-;;,_		Igor Peshansky, Ph.D. (name changed!)
     |,4-  ) )-,_. ,\ (  `'-'		old name: Igor Pechtchanski
    '---''(_/--'  `-'\_) fL	a.k.a JaguaR-R-R-r-r-r-.-.-.  Meow!

"Las! je suis sot... -Mais non, tu ne l'es pas, puisque tu t'en rends compte."
"But no -- you are no fool; you call yourself a fool, there's proof enough in
that!" -- Rostand, "Cyrano de Bergerac"
-------------- next part --------------
Index: winsup/utils/cygcheck.cc
===================================================================
RCS file: /cvs/src/src/winsup/utils/cygcheck.cc,v
retrieving revision 1.88
diff -u -p -r1.88 cygcheck.cc
--- winsup/utils/cygcheck.cc	19 Jan 2006 21:16:38 -0000	1.88
+++ winsup/utils/cygcheck.cc	3 Feb 2006 17:40:34 -0000
@@ -249,9 +249,41 @@ init_paths ()
     }
 }
 
+#define LINK_EXTENSION ".lnk"
+
+static bool
+check_existence (char *file, int showall, int foundone, char *first)
+{
+  if (GetFileAttributes (file) != (DWORD) - 1)
+    {
+      char *lastdot = strrchr (file, '.');
+      bool is_link = lastdot && !strcmp (lastdot, LINK_EXTENSION);
+      // If file is a link, fix up the extension before printing
+      if (is_link)
+	*lastdot = '\0';
+      if (showall)
+	printf ("Found: %s\n", file);
+      if (foundone)
+	{
+	  char *flastdot = strrchr (first, '.');
+	  bool f_is_link = flastdot && !strcmp (flastdot, LINK_EXTENSION);
+	  // if first is a link, fix up the extension before printing
+	  if (f_is_link)
+	    *flastdot = '\0';
+	  printf ("Warning: %s hides %s\n", first, file);
+	  if (f_is_link)
+	    *flastdot = '.';
+	}
+      if (is_link)
+	*lastdot = '.';
+      return true;
+    }
+  return false;
+}
+
 static char *
 find_on_path (char *file, char *default_extension,
-	      int showall = 0, int search_sysdirs = 0)
+	      int showall = 0, int search_sysdirs = 0, int checklinks = 0)
 {
   static char rv[4000];
   char tmp[4000], *ptr = rv;
@@ -270,11 +302,21 @@ find_on_path (char *file, char *default_
 
   if (strchr (file, ':') || strchr (file, '\\') || strchr (file, '/'))
     {
+      // FIXME: this will find "foo" before "foo.exe" -- contrary to Windows
       char *fn = cygpath (file, NULL);
       if (access (fn, F_OK) == 0)
 	return fn;
       strcpy (rv, fn);
       strcat (rv, default_extension);
+      if (access (rv, F_OK) == 0)
+	return rv;
+      if (!checklinks)
+	return fn;
+      strcat (rv, LINK_EXTENSION);
+      if (access (rv, F_OK) == 0)
+	return rv;
+      strcpy (rv, fn);
+      strcat (rv, LINK_EXTENSION);
       return access (rv, F_OK) == 0 ? rv : fn;
     }
 
@@ -286,14 +328,25 @@ find_on_path (char *file, char *default_
       if (i == 0 || !search_sysdirs || strcasecmp (paths[i], paths[0]))
 	{
 	  sprintf (ptr, "%s\\%s%s", paths[i], file, default_extension);
-	  if (GetFileAttributes (ptr) != (DWORD) - 1)
-	    {
-	      if (showall)
-		printf ("Found: %s\n", ptr);
-	      if (ptr == tmp && verbose)
-		printf ("Warning: %s hides %s\n", rv, ptr);
-	      ptr = tmp;
-	    }
+	  if (check_existence (ptr, showall, ptr == tmp && verbose, rv))
+	    ptr = tmp;
+
+	  if (!checklinks)
+	    continue;
+
+	  sprintf (ptr, "%s\\%s%s%s", paths[i], file, default_extension, LINK_EXTENSION);
+	  if (check_existence (ptr, showall, ptr == tmp && verbose, rv))
+	    ptr = tmp;
+
+	  if (!*default_extension)
+	    continue;
+
+	  sprintf (ptr, "%s\\%s", paths[i], file);
+	  if (check_existence (ptr, showall, ptr == tmp && verbose, rv))
+	    ptr = tmp;
+	  sprintf (ptr, "%s\\%s%s", paths[i], file, LINK_EXTENSION);
+	  if (check_existence (ptr, showall, ptr == tmp && verbose, rv))
+	    ptr = tmp;
 	}
     }
 
@@ -610,6 +663,101 @@ dll_info (const char *path, HANDLE fh, i
     cygwin_info (fh);
 }
 
+/* TODO: should the three functions below go into path.cc? */
+
+#define EXE_MAGIC ((int)*(unsigned short *)"MZ")
+#define SHORTCUT_MAGIC ((int)*(unsigned short *)"L\0")
+#define SYMLINK_COOKIE "!<symlink>"
+#define SYMLINK_MAGIC ((int)*(unsigned short *)SYMLINK_COOKIE)
+
+static bool
+is_exe (HANDLE fh)
+{
+  int magic = get_word (fh, 0x0);
+  return magic == EXE_MAGIC;
+}
+
+static bool
+is_symlink (HANDLE fh)
+{
+  int magic = get_word (fh, 0x0);
+  return magic == SHORTCUT_MAGIC || magic == SYMLINK_MAGIC;
+}
+
+static bool
+readlink (HANDLE fh, char *path, int maxlen)
+{
+  int got;
+  int magic = get_word (fh, 0x0);
+
+  if (magic == SHORTCUT_MAGIC)
+    {
+      int offset = get_word (fh, 0x4c);
+      int slen = get_word (fh, 0x4c + offset + 2);
+      if (slen >= maxlen)
+	{
+	  display_error ("readlink: Path too long");
+	  return false;
+	}
+      if (SetFilePointer (fh, 0x4c + offset + 4, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER
+	  && GetLastError () != NO_ERROR)
+	{
+	  display_error ("readlink: SetFilePointer()");
+	  return false;
+	}
+
+      if (!ReadFile (fh, path, slen, (DWORD *) &got, 0))
+	{
+	  display_error ("readlink: ReadFile()");
+	  return false;
+	}
+      else if (got < slen)
+	{
+	  display_error ("readlink: ReadFile()");
+	  return false;
+	}
+      else
+	path[got] = '\0';
+    }
+  else if (magic == SYMLINK_MAGIC)
+    {
+      char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
+
+      if (SetFilePointer (fh, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER
+	  && GetLastError () != NO_ERROR)
+	{
+	  display_error ("readlink: SetFilePointer()");
+	  return false;
+	}
+
+      if (!ReadFile (fh, cookie_buf, sizeof (cookie_buf), (DWORD *) &got, 0))
+	{
+	  display_error ("readlink: Readfile()");
+	  return false;
+	}
+      else if (got == sizeof (cookie_buf)
+	       && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
+	{
+	  if (!ReadFile (fh, path, maxlen, (DWORD *) &got, 0))
+	    {
+	      display_error ("readlink: ReadFile()");
+	      return false;
+	    }
+	  else if (got >= maxlen)
+	    {
+	      display_error ("readlink: Path too long");
+	      path[0] = '\0';
+	      return false;
+	    }
+	  else
+	    path[got] = '\0';
+	}
+    }
+  else
+    return false;
+  return true;
+}
+
 // Return true on success, false if error printed
 static bool
 track_down (char *file, char *suffix, int lvl)
@@ -682,7 +830,17 @@ track_down (char *file, char *suffix, in
 
   d->state = DID_ACTIVE;
 
-  dll_info (path, fh, lvl, 1);
+  if (is_exe (fh))
+    dll_info (path, fh, lvl, 1);
+  else if (is_symlink (fh))
+    display_error ("track_down: Huh?  Got a symlink!");
+  else
+    {
+      int magic = get_word (fh, 0x0);
+      magic &= 0x00FFFFFF;
+      printf (" - Not a DLL: magic number %x (%d) '%s'\n", magic, magic, (char *)&magic);
+    }
+
   d->state = DID_INACTIVE;
   if (!CloseHandle (fh))
     display_error ("track_down: CloseHandle()");
@@ -711,11 +869,55 @@ ls (char *f)
     display_error ("ls: CloseHandle()");
 }
 
+// Find a real application on the path (possibly following symlinks)
+static char *
+find_app_on_path (char *app, int showall = 0)
+{
+  char *papp = find_on_path (app, (char *) ".exe", showall, 0, 1);
+
+  HANDLE fh =
+    CreateFile (papp, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (fh == INVALID_HANDLE_VALUE)
+    {
+      printf (" - Cannot open\n");
+      return NULL;
+    }
+
+  if (is_symlink (fh))
+    {
+      static char tmp[4000] = "";
+      char *ptr;
+      readlink(fh, tmp, 3999);
+      ptr = cygpath(tmp, NULL);
+      for (char *p = ptr; (p = strchr (p, '/')); p++)
+	*p = '\\';
+      printf (" -> %s\n", ptr);
+      if (!strchr (ptr, '\\'))
+	{
+	  char *lastsep;
+	  strncpy (tmp, cygpath (papp, NULL), 3999);
+	  for (char *p = tmp; (p = strchr (p, '/')); p++)
+	    *p = '\\';
+	  lastsep = strrchr (tmp, '\\');
+	  strncpy(lastsep+1, ptr, 3999-(lastsep-tmp));
+	  ptr = tmp;
+	}
+      if (!CloseHandle (fh))
+	display_error ("find_app_on_path: CloseHandle()");
+      return find_app_on_path (ptr, showall);
+    }
+
+  if (!CloseHandle (fh))
+    display_error ("find_app_on_path: CloseHandle()");
+  return papp;
+}
+
 // Return true on success, false if error printed
 static bool
 cygcheck (char *app)
 {
-  char *papp = find_on_path (app, (char *) ".exe", 1, 0);
+  char *papp = find_app_on_path (app, 1);
   if (!papp)
     {
       printf ("Error: could not find %s\n", app);
@@ -1441,7 +1643,7 @@ dump_sysinfo ()
     printf
       ("Looking to see where common programs can be found, if at all...\n");
   for (i = 0; common_apps[i].name; i++)
-    if (!find_on_path ((char *) common_apps[i].name, (char *) ".exe", 1, 0))
+    if (!find_app_on_path ((char *) common_apps[i].name, 1))
       {
 	if (common_apps[i].missing_is_good)
 	  printf ("Not Found: %s (good!)\n", common_apps[i].name);


More information about the Cygwin-patches mailing list