diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2001-02-22 15:56:36 +0300 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2001-02-22 15:56:36 +0300 |
commit | 79e56091c9e4f21ecf1807e0f0f4441ddddc84e6 (patch) | |
tree | 323f92d808a31d48a656b112b8cb031aa1fd45b6 /winsup/cygwin/shortcut.c | |
parent | 957059c8315fd725db2096ef696e57167d721e31 (diff) |
* shortcut.c: New file. Provides a C interface to reading of
Windows shortcuts to avoid compiler flag `-fvtable-thunks'.
* shortcut.h: Ditto.
* Makefile.in: Add shortcut.o to DLL_OFILES.
* cygerrno.h: Provide a C interface to `geterrno_from_win_error' for
using in shortcut.c.
* errno.cc (geterrno_from_win_error): Define as extern "C".
* path.cc (struct symlink_info): Remove methods `check_shortcut' and
`check_sysfile'.
(shortcut_header): Move to shortcut.c.
(shortcut_initalized): Ditto.
(create_shortcut_header): Ditto.
(cmp_shortcut_header): Ditto.
(symlink_info::check_shortcut): Ditto. Reorganize as a plain C function.
(symlink_info::check_sysfile): Redefine as a global function using the
same parameter list as `check_shortcut' for clearness.
(symlink_info::check): Change parameter list for calls to
`check_shortcut' and `check_sysfile'.
Diffstat (limited to 'winsup/cygwin/shortcut.c')
-rw-r--r-- | winsup/cygwin/shortcut.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/winsup/cygwin/shortcut.c b/winsup/cygwin/shortcut.c new file mode 100644 index 000000000..7c05f9db3 --- /dev/null +++ b/winsup/cygwin/shortcut.c @@ -0,0 +1,171 @@ +/* shortcut.c: Read shortcuts. This part of the code must be in C because + the C++ interface to COM doesn't work without -fvtable-thunk + which is too dangerous to use. + + Copyright 2001 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. */ + +#include "winsup.h" +#include <shlobj.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/mount.h> +#include <errno.h> +#include "cygerrno.h" +#include "shortcut.h" + +/* This is needed to avoid including path.h which is a pure C++ header. */ +#define PATH_SYMLINK MOUNT_SYMLINK + +#define debug_printf(x) strcpy (contents, x) + +char shortcut_header[SHORTCUT_HDR_SIZE]; +BOOL shortcut_initalized = FALSE; + +void +create_shortcut_header (void) +{ + if (!shortcut_initalized) + { + shortcut_header[0] = 'L'; + shortcut_header[4] = '\001'; + shortcut_header[5] = '\024'; + shortcut_header[6] = '\002'; + shortcut_header[12] = '\300'; + shortcut_header[19] = 'F'; + shortcut_header[20] = '\f'; + shortcut_header[60] = '\001'; + shortcut_initalized = TRUE; + } +} + +static BOOL +cmp_shortcut_header (const char *file_header) +{ + create_shortcut_header (); + return memcmp (shortcut_header, file_header, SHORTCUT_HDR_SIZE); +} + +int +check_shortcut (const char *path, DWORD fileattr, HANDLE h, + char *contents, int *error, unsigned *pflags) +{ + HRESULT hres; + IShellLink *psl = NULL; + IPersistFile *ppf = NULL; + WCHAR wc_path[MAX_PATH]; + char full_path[MAX_PATH]; + WIN32_FIND_DATA wfd; + DWORD len = 0; + int res = 0; + + /* Initialize COM library. */ + CoInitialize (NULL); + + /* Get a pointer to the IShellLink interface. */ + hres = CoCreateInstance (&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + &IID_IShellLink, (void **)&psl); + if (FAILED (hres)) + { + debug_printf ("CoCreateInstance failed"); + goto close_it; + } + /* Get a pointer to the IPersistFile interface. */ + hres = psl->lpVtbl->QueryInterface (psl, &IID_IPersistFile, (void **)&ppf); + if (FAILED (hres)) + { + debug_printf ("QueryInterface failed"); + goto close_it; + } + /* Load the shortcut. */ + MultiByteToWideChar(CP_ACP, 0, path, -1, wc_path, MAX_PATH); + hres = ppf->lpVtbl->Load (ppf, wc_path, STGM_READ); + if (FAILED (hres)) + { + debug_printf ("Load failed"); + goto close_it; + } + /* Try the description (containing a POSIX path) first. */ + if (fileattr & FILE_ATTRIBUTE_READONLY) + { + /* An additional check is needed to prove if it's a shortcut + really created by Cygwin or U/WIN. */ + char file_header[SHORTCUT_HDR_SIZE]; + DWORD got; + + if (! ReadFile (h, file_header, SHORTCUT_HDR_SIZE, &got, 0)) + { + debug_printf ("ReadFile failed"); + *error = EIO; + goto close_it_dont_set_error; + } + if (got == SHORTCUT_HDR_SIZE && !cmp_shortcut_header (file_header)) + { + hres = psl->lpVtbl->GetDescription (psl, contents, MAX_PATH); + if (FAILED (hres)) + { + debug_printf ("GetDescription failed"); + goto close_it; + } + len = strlen (contents); + } + } + /* No description or not R/O: Check the "official" path. */ + if (len == 0) + { + /* Convert to full path (easy way) */ + if ((path[0] == '\\' && path[1] == '\\') + || (_toupper (path[0]) >= 'A' && _toupper (path[0]) <= 'Z' + && path[1] == ':')) + len = 0; + else + { + len = GetCurrentDirectory (MAX_PATH, full_path); + if (path[0] == '\\') + len = 2; + else if (full_path[len - 1] != '\\') + strcpy (full_path + len++, "\\"); + } + strcpy (full_path + len, path); + /* Set relative path inside of IShellLink interface. */ + hres = psl->lpVtbl->SetRelativePath (psl, full_path, 0); + if (FAILED (hres)) + { + debug_printf ("SetRelativePath failed"); + goto close_it; + } + /* Get the path to the shortcut target. */ + hres = psl->lpVtbl->GetPath (psl, contents, MAX_PATH, &wfd, 0); + if (FAILED(hres)) + { + debug_printf ("GetPath failed"); + goto close_it; + } + } + /* It's a symlink. */ + *pflags = PATH_SYMLINK; + res = strlen (contents); + +close_it: + if (FAILED (hres)) + *error = geterrno_from_win_error (HRESULT_CODE (hres), EACCES); + +close_it_dont_set_error: + /* Release the pointer to IPersistFile. */ + if (ppf) + ppf->lpVtbl->Release(ppf); + /* Release the pointer to IShellLink. */ + if (psl) + psl->lpVtbl->Release(psl); + /* Uninitialize COM library. */ + CoUninitialize (); + + return res; +} + + |