[PATCH] cygcheck: follow symbolic links
Igor Peshansky
pechtcha@cs.nyu.edu
Wed Feb 22 18:55:00 GMT 2006
On Fri, 17 Feb 2006, Igor Peshansky wrote:
> On Fri, 17 Feb 2006, Corinna Vinschen wrote:
>
> > On Feb 16 12:26, Igor Peshansky wrote:
> > > On Thu, 16 Feb 2006, Corinna Vinschen wrote:
> > > > - Most of your patch should go into path.cc so it can be reused,
> > > > for instance in strace.
> > >
> > > Agreed -- that's why I put that TODO in there. :-) Should I move it
> > > in the next iteration of the patch?
> >
> > Please move it now. I don't think it's non-trivial enough to justify
> > multiple iterations.
>
> Whoops. Misspoke. I meant "incarnation". Never mind, I'll just do it.
> :-) Expect a new patch today.
I guess "today" is a stretchable concept. :-) In any case, here's a new
patch. Moving things into path.cc turned out to be indeed non-trivial,
since the new functionality was using static functions in cygcheck.cc
which now needed to be moved out into a separate file. I don't expect
this to be applied right away (hence no ChangeLog), but is this along the
lines of what you were expecting?
> > > > - Couldn't you just reuse the readlink implementation in
> > > > ../cygwin/path.cc as is, to avoid having to different
> > > > implementations?
> > >
> > > Umm, most of that code is very general purpose, and has too much
> > > extra stuff in it. I basically used part of it
> > > (symlink_info::check_shortcut) for my implementation. I wanted
> > > something lightweight and easy to understand (also, the code in
> > > path.cc doesn't check for PE headers, so I had to write that part
> > > anyway).
> >
> > Well, what I meant isn't readlink but symlink_info::check_shortcut and
> > cmp_shortcut_header. It would be helpful if the rules to identify a
> > symlink are identical, wouldn't it? As for the PE headers, that's
> > fine.
>
> It would certainly help, but then we would need to extract the bit of
> code that deals with symlinks and put it in a Cygwin-independent static
> library. See my reply to Dave.
I did copy cmp_shortcut_header, but most of the rest of the code was
judicious cut-and-paste (with some rewriting on the side). Again, it
wasn't as trivial as you made it sound.
Igor
--
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: 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 <stdio.h>
+#include <windows.h>
+#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 "!<symlink>"
+#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;
More information about the Cygwin-patches
mailing list