diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2006-08-02 09:48:18 +0000 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2006-08-02 09:48:18 +0000 |
commit | 2dba45f4aab6206025bb2f09c6e6a68d20386628 (patch) | |
tree | 88ff1d3191d02562f91039e3f172a25326c88337 /winsup/utils/cygpath.cc | |
parent | 2ebf115df97ff9ad5d72fe7723f934e0104b0e08 (diff) | |
download | newlib-2dba45f4aab6206025bb2f09c6e6a68d20386628.zip newlib-2dba45f4aab6206025bb2f09c6e6a68d20386628.tar.gz newlib-2dba45f4aab6206025bb2f09c6e6a68d20386628.tar.bz2 |
* Makefile.in (cygpath.exe): Add rule to link cygpath against ntdll.dll.
* cygpath.cc: Include DDK headers.
(RtlAllocateUnicodeString): New static inline function.
(get_device_name): New static function to evaluate DOS path from
native NT path.
(get_device_paths): New function to do the same for path lists.
(doit): Call get_device_paths/get_device_name where appropriate.
Diffstat (limited to 'winsup/utils/cygpath.cc')
-rw-r--r-- | winsup/utils/cygpath.cc | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/winsup/utils/cygpath.cc b/winsup/utils/cygpath.cc index 96738b1..9d0f80d 100644 --- a/winsup/utils/cygpath.cc +++ b/winsup/utils/cygpath.cc @@ -21,6 +21,9 @@ details. */ #include <sys/cygwin.h> #include <ctype.h> #include <errno.h> +#include <ddk/ntddk.h> +#include <ddk/winddk.h> +#include <ddk/ntifs.h> static const char version[] = "$Revision$"; @@ -109,6 +112,161 @@ Other options:\n\ exit (ignore_flag ? 0 : status); } +static inline BOOLEAN +RtlAllocateUnicodeString (PUNICODE_STRING uni, ULONG size) +{ + uni->Length = 0; + uni->MaximumLength = 512; + uni->Buffer = (WCHAR *) malloc (size); + return uni->Buffer != NULL; +} + +static char * +get_device_name (char *path) +{ + UNICODE_STRING ntdev, tgtdev, ntdevdir; + ANSI_STRING ans; + OBJECT_ATTRIBUTES ntobj; + NTSTATUS status; + HANDLE lnk, dir; + char *ret = strdup (path); + PDIRECTORY_BASIC_INFORMATION odi = (PDIRECTORY_BASIC_INFORMATION) + alloca (4096); + BOOLEAN restart; + ULONG cont; + + if (strncasecmp (path, "\\Device\\", 8)) + return ret; + + if (!RtlAllocateUnicodeString (&ntdev, MAX_PATH * 2)) + return ret; + if (!RtlAllocateUnicodeString (&tgtdev, MAX_PATH * 2)) + return ret; + RtlInitAnsiString (&ans, path); + RtlAnsiStringToUnicodeString (&ntdev, &ans, FALSE); + + /* First check if the given device name is a symbolic link itself. If so, + query it and use the new name as actual device name to search for in the + DOS device name directory. If not, just use the incoming device name. */ + InitializeObjectAttributes (&ntobj, &ntdev, OBJ_CASE_INSENSITIVE, NULL, NULL); + status = ZwOpenSymbolicLinkObject (&lnk, SYMBOLIC_LINK_QUERY, &ntobj); + if (NT_SUCCESS (status)) + { + status = ZwQuerySymbolicLinkObject (lnk, &tgtdev, NULL); + ZwClose (lnk); + if (!NT_SUCCESS (status)) + goto out; + RtlCopyUnicodeString (&ntdev, &tgtdev); + } + else if (status != STATUS_OBJECT_TYPE_MISMATCH) + goto out; + + for (int i = 0; i < 2; ++i) + { + /* There are two DOS device directories, the local and the global dir. + Try both, local first. */ + RtlInitUnicodeString (&ntdevdir, i ? L"\\GLOBAL??" : L"\\??"); + + /* Open the directory... */ + InitializeObjectAttributes (&ntobj, &ntdevdir, OBJ_CASE_INSENSITIVE, + NULL, NULL); + status = ZwOpenDirectoryObject (&dir, DIRECTORY_QUERY, &ntobj); + if (!NT_SUCCESS (status)) + break; + + /* ...and scan it. */ + for (restart = TRUE, cont = 0; + NT_SUCCESS (ZwQueryDirectoryObject (dir, odi, 4096, TRUE, + restart, &cont, NULL)); + restart = FALSE) + { + /* For each entry check if it's a symbolic link. */ + InitializeObjectAttributes (&ntobj, &odi->ObjectName, + OBJ_CASE_INSENSITIVE, dir, NULL); + status = ZwOpenSymbolicLinkObject (&lnk, SYMBOLIC_LINK_QUERY, &ntobj); + if (!NT_SUCCESS (status)) + continue; + tgtdev.Length = 0; + tgtdev.MaximumLength = 512; + /* If so, query it and compare the target of the symlink with the + incoming device name. */ + status = ZwQuerySymbolicLinkObject (lnk, &tgtdev, NULL); + ZwClose (lnk); + if (!NT_SUCCESS (status)) + continue; + if (RtlEqualUnicodeString (&ntdev, &tgtdev, TRUE)) + { + /* If the comparison succeeds, the name of the directory entry is + a valid DOS device name, if prepended with "\\.\". Return that + valid DOS path. */ + ULONG len = RtlUnicodeStringToAnsiSize (&odi->ObjectName); + ret = (char *) malloc (len + 4); + strcpy (ret, "\\\\.\\"); + ans.Length = 0; + ans.MaximumLength = len; + ans.Buffer = ret + 4; + RtlUnicodeStringToAnsiString (&ans, &odi->ObjectName, FALSE); + ZwClose (dir); + goto out; + } + } + ZwClose (dir); + } + +out: + free (tgtdev.Buffer); + free (ntdev.Buffer); + return ret; +} + +static char * +get_device_paths (char *path) +{ + char *sbuf; + char *ptr; + int n = 1; + + ptr = path; + while ((ptr = strchr (ptr, ';'))) + { + ptr++; + n++; + } + + char *paths[n]; + DWORD acc = 0; + int i; + if (!n) + return strdup (""); + + for (i = 0, ptr = path; ptr; i++) + { + char *next = ptr; + ptr = strchr (ptr, ';'); + if (ptr) + *ptr++ = 0; + paths[i] = get_device_name (next); + acc += strlen (paths[i]) + 1; + } + + sbuf = (char *) malloc (acc + 1); + if (sbuf == NULL) + { + fprintf (stderr, "%s: out of memory\n", prog_name); + exit (1); + } + + sbuf[0] = '\0'; + for (i = 0; i < n; i++) + { + strcat (strcat (sbuf, paths[i]), ";"); + free (paths[i]); + } + + strchr (sbuf, '\0')[-1] = '\0'; + return sbuf; +} + static char * get_short_paths (char *path) { @@ -487,6 +645,7 @@ doit (char *filename) err = cygwin_posix_to_win32_path_list (filename, buf); if (err) /* oops */; + buf = get_device_paths (buf); if (shortname_flag) buf = get_short_paths (buf); if (longname_flag) @@ -518,6 +677,7 @@ doit (char *filename) } if (!unix_flag) { + buf = get_device_name (buf); if (shortname_flag) buf = get_short_name (buf); if (longname_flag) |