diff options
Diffstat (limited to 'winsup/utils/path.cc')
-rw-r--r-- | winsup/utils/path.cc | 202 |
1 files changed, 200 insertions, 2 deletions
diff --git a/winsup/utils/path.cc b/winsup/utils/path.cc index 57bd3a4ff..356a60f01 100644 --- a/winsup/utils/path.cc +++ b/winsup/utils/path.cc @@ -9,14 +9,16 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for 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) #include <windows.h> #include <stdio.h> #include <stdlib.h> +#include "path.h" #include "cygwin/include/cygwin/version.h" #include "cygwin/include/sys/mount.h" #include "cygwin/include/mntent.h" @@ -29,6 +31,202 @@ 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; +} + +int +get_word (HANDLE fh, int offset) +{ + unsigned short rv; + unsigned r; + + SetLastError(NO_ERROR); + if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER + && GetLastError () != NO_ERROR) + return -1; + + if (!ReadFile (fh, &rv, 2, (DWORD *) &r, 0)) + return -1; + + return rv; +} + +/* + * Check the value of GetLastError() to find out whether there was an error. + */ +int +get_dword (HANDLE fh, int offset) +{ + int rv; + unsigned r; + + SetLastError(NO_ERROR); + if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER + && GetLastError () != NO_ERROR) + return -1; + + if (!ReadFile (fh, &rv, 4, (DWORD *) &r, 0)) + return -1; + + return rv; +} + +#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 symlink_info::check() in ../cygwin/path.cc) */ + } + 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) + { + SetLastError (ERROR_FILENAME_EXCED_RANGE); + return false; + } + if (SetFilePointer (fh, 0x4c + offset + 4, 0, FILE_BEGIN) == + INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) + return false; + + if (!ReadFile (fh, path, slen, (DWORD *) &got, 0)) + return false; + else if (got < slen) + { + SetLastError (ERROR_READ_FAULT); + 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) + return false; + + if (!ReadFile (fh, cookie_buf, sizeof (cookie_buf), (DWORD *) &got, 0)) + 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)) + return false; + else if (got >= maxlen) + { + SetLastError (ERROR_FILENAME_EXCED_RANGE); + path[0] = '\0'; + return false; + } + else + path[got] = '\0'; + } + } + else + return false; + return true; +} + static struct mnt { const char *native; |