Index: Makefile.in =================================================================== RCS file: /cvs/src/src/winsup/utils/Makefile.in,v retrieving revision 1.62 diff -u -p -r1.62 Makefile.in --- Makefile.in 18 Jan 2006 15:57:55 -0000 1.62 +++ Makefile.in 22 Feb 2006 18:49:42 -0000 @@ -91,23 +91,23 @@ endif all: Makefile $(PROGS) -strace.exe: strace.o path.o $(MINGW_DEP_LDLIBS) +strace.exe: strace.o path.o fileutil.o $(MINGW_DEP_LDLIBS) ifdef VERBOSE - $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,2,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) + $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,3,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) else - @echo $(CXX) -o $@ ${wordlist 1,2,$^} ${filter-out -B%, $(MINGW_CXXFLAGS) $(MINGW_LDFLAGS)};\ - $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,2,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) + @echo $(CXX) -o $@ ${wordlist 1,3,$^} ${filter-out -B%, $(MINGW_CXXFLAGS) $(MINGW_LDFLAGS)};\ + $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,3,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) endif -cygcheck.exe: cygcheck.o path.o dump_setup.o $(MINGW_DEP_LDLIBS) +cygcheck.exe: cygcheck.o path.o fileutil.o dump_setup.o $(MINGW_DEP_LDLIBS) ifeq "$(libz)" "" @echo '*** Building cygcheck without package content checking due to missing mingw libz.a.' endif ifdef VERBOSE - $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,3,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) $(libz) + $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,4,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) $(libz) else - @echo $(CXX) -o $@ ${wordlist 1,3,$^} ${filter-out -B%, $(MINGW_CXXFLAGS) $(MINGW_LDFLAGS)} $(libz);\ - $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,3,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) $(libz) + @echo $(CXX) -o $@ ${wordlist 1,4,$^} ${filter-out -B%, $(MINGW_CXXFLAGS) $(MINGW_LDFLAGS)} $(libz);\ + $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,4,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) $(libz) endif dumper.o: dumper.cc dumper.h @@ -150,6 +150,14 @@ else $(MINGW_CXX) $(zconf_h) $(zlib_h) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) $< endif +fileutil.o: fileutil.cc +ifdef VERBOSE + $(MINGW_CXX) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) $< +else + @echo $(MINGW_CXX) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) ... $^;\ + ${MINGW_CXX} $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) $< +endif + cygcheck.o: cygcheck.cc ifdef VERBOSE ${MINGW_CXX} $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) -I$(updir) $< Index: cygcheck.cc =================================================================== RCS file: /cvs/src/src/winsup/utils/cygcheck.cc,v retrieving revision 1.90 diff -u -p -r1.90 cygcheck.cc --- cygcheck.cc 8 Feb 2006 14:19:40 -0000 1.90 +++ cygcheck.cc 22 Feb 2006 18:49:42 -0000 @@ -52,6 +52,10 @@ void dump_setup (int, char **, bool); void package_find (int, char **); void package_list (int, char **); +int get_word (HANDLE, int); +int get_dword (HANDLE, int); +int display_error (const char *, bool show_error = true, bool print_failed = true); + static const char version[] = "$Revision: 1.90 $"; static const char *known_env_vars[] = { @@ -126,21 +130,6 @@ eprintf (const char *format, ...) va_end (ap); } -/* - * display_error() is used to report failure modes - */ -static int -display_error (const char *name, bool show_error = true, bool print_failed = true) -{ - if (show_error) - fprintf (stderr, "cygcheck: %s%s: %lu\n", name, - print_failed ? " failed" : "", GetLastError ()); - else - fprintf (stderr, "cygcheck: %s%s\n", name, - print_failed ? " failed" : ""); - return 1; -} - /* Display a WinInet error message, and close a variable number of handles. (Passed a list of handles terminated by NULL.) */ static int @@ -249,9 +238,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 +291,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 ? strdup (rv) : fn; } @@ -286,14 +317,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; } } @@ -330,38 +372,6 @@ already_did (char *file) return d; } -static int -get_word (HANDLE fh, int offset) -{ - short rv; - unsigned r; - - if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER - && GetLastError () != NO_ERROR) - display_error ("get_word: SetFilePointer()"); - - if (!ReadFile (fh, &rv, 2, (DWORD *) &r, 0)) - display_error ("get_word: Readfile()"); - - return rv; -} - -static int -get_dword (HANDLE fh, int offset) -{ - int rv; - unsigned r; - - if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER - && GetLastError () != NO_ERROR) - display_error ("get_dword: SetFilePointer()"); - - if (!ReadFile (fh, &rv, 4, (DWORD *) &r, 0)) - display_error ("get_dword: Readfile()"); - - return rv; -} - struct Section { char name[8]; @@ -610,6 +620,10 @@ dll_info (const char *path, HANDLE fh, i cygwin_info (fh); } +extern int is_exe (HANDLE); +extern int is_symlink (HANDLE); +extern int readlink (HANDLE, char *, int); + // Return true on success, false if error printed static bool track_down (char *file, char *suffix, int lvl) @@ -682,7 +696,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 +735,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 +1509,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); Index: fileutil.cc =================================================================== RCS file: fileutil.cc diff -N fileutil.cc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ fileutil.cc 22 Feb 2006 18:49:42 -0000 @@ -0,0 +1,62 @@ +/* fileutil.cc + + Copyright 2006 Red Hat, Inc. + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#define cygwin_internal cygwin_internal_dontuse +#include +#include +#undef cygwin_internal + +/* + * display_error() is used to report failure modes + */ +int +display_error (const char *name, bool show_error = true, bool print_failed = true) +{ + if (show_error) + fprintf (stderr, "cygcheck: %s%s: %lu\n", name, + print_failed ? " failed" : "", GetLastError ()); + else + fprintf (stderr, "cygcheck: %s%s\n", name, + print_failed ? " failed" : ""); + return 1; +} + +int +get_word (HANDLE fh, int offset) +{ + short rv; + unsigned r; + + if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER + && GetLastError () != NO_ERROR) + display_error ("get_word: SetFilePointer()"); + + if (!ReadFile (fh, &rv, 2, (DWORD *) &r, 0)) + display_error ("get_word: Readfile()"); + + return rv; +} + +int +get_dword (HANDLE fh, int offset) +{ + int rv; + unsigned r; + + if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER + && GetLastError () != NO_ERROR) + display_error ("get_dword: SetFilePointer()"); + + if (!ReadFile (fh, &rv, 4, (DWORD *) &r, 0)) + display_error ("get_dword: Readfile()"); + + return rv; +} + Index: path.cc =================================================================== RCS file: /cvs/src/src/winsup/utils/path.cc,v retrieving revision 1.9 diff -u -p -r1.9 path.cc --- path.cc 29 Apr 2005 16:39:34 -0000 1.9 +++ path.cc 22 Feb 2006 18:49:43 -0000 @@ -9,8 +9,9 @@ Cygwin license. Please consult the file details. */ /* The purpose of this file is to hide all the details about accessing - Cygwin's mount table. If the format or location of the mount table - changes, this is the file to change to match it. */ + Cygwin's mount table, shortcuts, etc. If the format or location of + the mount table, or the shortcut format changes, this is the file to + change to match it. */ #define str(a) #a #define scat(a,b) str(a##b) @@ -21,6 +22,9 @@ details. */ #include "cygwin/include/sys/mount.h" #include "cygwin/include/mntent.h" +int get_word (HANDLE, int); +int display_error (const char *, bool show_error = true, bool print_failed = true); + /* Used when treating / and \ as equivalent. */ #define SLASH_P(ch) \ ({ \ @@ -29,6 +33,178 @@ details. */ }) +static const GUID GUID_shortcut + = { 0x00021401L, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46 }; + +enum { + WSH_FLAG_IDLIST = 0x01, /* Contains an ITEMIDLIST. */ + WSH_FLAG_FILE = 0x02, /* Contains a file locator element. */ + WSH_FLAG_DESC = 0x04, /* Contains a description. */ + WSH_FLAG_RELPATH = 0x08, /* Contains a relative path. */ + WSH_FLAG_WD = 0x10, /* Contains a working dir. */ + WSH_FLAG_CMDLINE = 0x20, /* Contains command line args. */ + WSH_FLAG_ICON = 0x40 /* Contains a custom icon. */ +}; + +struct win_shortcut_hdr + { + DWORD size; /* Header size in bytes. Must contain 0x4c. */ + GUID magic; /* GUID of shortcut files. */ + DWORD flags; /* Content flags. See above. */ + + /* The next fields from attr to icon_no are always set to 0 in Cygwin + and U/Win shortcuts. */ + DWORD attr; /* Target file attributes. */ + FILETIME ctime; /* These filetime items are never touched by the */ + FILETIME mtime; /* system, apparently. Values don't matter. */ + FILETIME atime; + DWORD filesize; /* Target filesize. */ + DWORD icon_no; /* Icon number. */ + + DWORD run; /* Values defined in winuser.h. Use SW_NORMAL. */ + DWORD hotkey; /* Hotkey value. Set to 0. */ + DWORD dummy[2]; /* Future extension probably. Always 0. */ + }; + +static bool +cmp_shortcut_header (win_shortcut_hdr *file_header) +{ + /* A Cygwin or U/Win shortcut only contains a description and a relpath. + Cygwin shortcuts also might contain an ITEMIDLIST. The run type is + always set to SW_NORMAL. */ + return file_header->size == sizeof (win_shortcut_hdr) + && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut) + && (file_header->flags & ~WSH_FLAG_IDLIST) + == (WSH_FLAG_DESC | WSH_FLAG_RELPATH) + && file_header->run == SW_NORMAL; +} + +#define EXE_MAGIC ((int)*(unsigned short *)"MZ") +#define SHORTCUT_MAGIC ((int)*(unsigned short *)"L\0") +#define SYMLINK_COOKIE "!" +#define SYMLINK_MAGIC ((int)*(unsigned short *)SYMLINK_COOKIE) + +bool +is_exe (HANDLE fh) +{ + int magic = get_word (fh, 0x0); + return magic == EXE_MAGIC; +} + +bool +is_symlink (HANDLE fh) +{ + int magic = get_word (fh, 0x0); + if (magic != SHORTCUT_MAGIC && magic != SYMLINK_MAGIC) + return false; + DWORD got; + BY_HANDLE_FILE_INFORMATION local; + if (!GetFileInformationByHandle (fh, &local)) + return false; + if (magic == SHORTCUT_MAGIC) + { + DWORD size; + if (!local.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + return false; /* Not a Cygwin symlink. */ + if ((size = GetFileSize (fh, NULL)) > 8192) + return false; /* Not a Cygwin symlink. */ + char buf[size]; + SetFilePointer (fh, 0, 0, FILE_BEGIN); + if (!ReadFile (fh, buf, size, &got, 0)) + return false; + if (got != size || !cmp_shortcut_header ((win_shortcut_hdr *) buf)) + return false; /* Not a Cygwin symlink. */ + /* TODO: check for invalid path contents (see ../cygwin/path.cc:3313 */ + } + else /* magic == SYMLINK_MAGIC */ + { + if (!local.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) + return false; /* Not a Cygwin symlink. */ + char buf[sizeof (SYMLINK_COOKIE) - 1]; + SetFilePointer (fh, 0, 0, FILE_BEGIN); + if (!ReadFile (fh, buf, sizeof (buf), &got, 0)) + return false; + if (got != sizeof (buf) || memcmp (buf, SYMLINK_COOKIE, sizeof (buf)) != 0) + return false; /* Not a Cygwin symlink. */ + } + return true; +} + +/* Assumes is_symlink(fh) is true */ +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; +} + static struct mnt { const char *native;