* cygpath.cc (RtlEqualUnicodePathPrefix): New helper function.

(HARDDISK_PREFIX): Move.
	(GLOBALROOT_PREFIX): Define.
	(get_device_name): Take GLOBALROOT_PREFIX into account.
	Improve check for path to allow filesystem access via block devices.
	Potentially drop \\.\ prefix if resulting path is a valid DOS
	pathname.
	(do_pathconv): Make sure to drop \\?\ prefix only if path is
	actually a filesystem based path.
	(print_version): Fix copyright.
This commit is contained in:
Corinna Vinschen 2010-09-06 09:48:55 +00:00
parent 43f65cdd7d
commit d2cc418e6e
2 changed files with 73 additions and 10 deletions

View File

@ -1,3 +1,16 @@
2010-09-06 Corinna Vinschen <corinna@vinschen.de>
* cygpath.cc (RtlEqualUnicodePathPrefix): New helper function.
(HARDDISK_PREFIX): Move.
(GLOBALROOT_PREFIX): Define.
(get_device_name): Take GLOBALROOT_PREFIX into account.
Improve check for path to allow filesystem access via block devices.
Potentially drop \\.\ prefix if resulting path is a valid DOS
pathname.
(do_pathconv): Make sure to drop \\?\ prefix only if path is
actually a filesystem based path.
(print_version): Fix copyright.
2010-08-28 Corinna Vinschen <corinna@vinschen.de>
* ldh.cc (WinMain): Change DONT_RESOLVE_DLL_REFERENCES to

View File

@ -134,11 +134,23 @@ static inline BOOLEAN
RtlAllocateUnicodeString (PUNICODE_STRING uni, ULONG size)
{
uni->Length = 0;
uni->MaximumLength = 512;
uni->MaximumLength = size / sizeof (WCHAR);
uni->Buffer = (WCHAR *) malloc (size);
return uni->Buffer != NULL;
}
static inline BOOLEAN
RtlEqualUnicodePathPrefix (PUNICODE_STRING path, PUNICODE_STRING prefix,
BOOLEAN caseinsensitive)
{
UNICODE_STRING p;
p.Length = p.MaximumLength = prefix->Length < path->Length
? prefix->Length : path->Length;
p.Buffer = path->Buffer;
return RtlEqualUnicodeString (&p, prefix, caseinsensitive);
}
static size_t
my_wcstombs (char *dest, const wchar_t *src, size_t n)
{
@ -148,6 +160,9 @@ my_wcstombs (char *dest, const wchar_t *src, size_t n)
return wcstombs (dest, src, n);
}
#define HARDDISK_PREFIX L"\\Device\\Harddisk"
#define GLOBALROOT_PREFIX "\\\\.\\GLOBALROOT"
static char *
get_device_name (char *path)
{
@ -156,18 +171,21 @@ get_device_name (char *path)
OBJECT_ATTRIBUTES ntobj;
NTSTATUS status;
HANDLE lnk, dir;
bool got_one = false;
char *ret = strdup (path);
PDIRECTORY_BASIC_INFORMATION odi = (PDIRECTORY_BASIC_INFORMATION)
alloca (4096);
BOOLEAN restart;
ULONG cont;
if (!strncasecmp (path, GLOBALROOT_PREFIX "\\", sizeof (GLOBALROOT_PREFIX)))
path += sizeof (GLOBALROOT_PREFIX) - 1;
if (strncasecmp (path, "\\Device\\", 8))
return ret;
if (!RtlAllocateUnicodeString (&ntdev, 65536))
if (!RtlAllocateUnicodeString (&ntdev, 65534))
return ret;
if (!RtlAllocateUnicodeString (&tgtdev, 65536))
if (!RtlAllocateUnicodeString (&tgtdev, 65534))
return ret;
RtlInitAnsiString (&ans, path);
RtlAnsiStringToUnicodeString (&ntdev, &ans, FALSE);
@ -185,7 +203,8 @@ get_device_name (char *path)
goto out;
RtlCopyUnicodeString (&ntdev, &tgtdev);
}
else if (status != STATUS_OBJECT_TYPE_MISMATCH)
else if (status != STATUS_OBJECT_TYPE_MISMATCH
&& status != STATUS_OBJECT_PATH_SYNTAX_BAD)
goto out;
for (int i = 0; i < 2; ++i)
@ -221,12 +240,19 @@ get_device_name (char *path)
ZwClose (lnk);
if (!NT_SUCCESS (status))
continue;
if (RtlEqualUnicodeString (&ntdev, &tgtdev, TRUE))
if (tgtdev.Length /* There's actually a symlink pointing to an
empty string: \??\GLOBALROOT -> "" */
&& RtlEqualUnicodePathPrefix (&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. */
wchar_t *trailing = NULL;
if (ntdev.Length > tgtdev.Length)
trailing = ntdev.Buffer + tgtdev.Length / sizeof (WCHAR);
ULONG len = RtlUnicodeStringToAnsiSize (&odi->ObjectName);
if (trailing)
len += my_wcstombs (NULL, trailing, 0);
free (ret);
ret = (char *) malloc (len + 4);
strcpy (ret, "\\\\.\\");
@ -234,15 +260,30 @@ get_device_name (char *path)
ans.MaximumLength = len;
ans.Buffer = ret + 4;
RtlUnicodeStringToAnsiString (&ans, &odi->ObjectName, FALSE);
if (trailing)
my_wcstombs (ans.Buffer + ans.Length, trailing,
ans.MaximumLength - ans.Length);
ans.Buffer[ans.MaximumLength - 1] = '\0';
got_one = true;
/* Special case for local disks: It's most feasible if the
DOS device name reflects the DOS drive, so we check for this
explicitly and only return prematurely if so. */
#define HARDDISK_PREFIX L"\\Device\\Harddisk"
if (ntdev.Length < wcslen (HARDDISK_PREFIX)
|| wcsncasecmp (ntdev.Buffer, HARDDISK_PREFIX, 8) != 0
|| (odi->ObjectName.Length == 2 * sizeof (WCHAR)
&& odi->ObjectName.Buffer[1] == L':'))
{
if (trailing)
{
/* If there's a trailing path, it's a perfectly valid
DOS pathname without the \\.\ prefix. Unless it's
longer than MAX_PATH - 1 in which case it needs
the \\?\ prefix. */
if (len = strlen (ret + 4) >= MAX_PATH)
ret[2] = '?';
else
memmove (ret, ret + 4, strlen (ret + 4) + 1);
}
ZwClose (dir);
goto out;
}
@ -254,6 +295,13 @@ get_device_name (char *path)
out:
free (tgtdev.Buffer);
free (ntdev.Buffer);
if (!got_one)
{
free (ret);
ret = (char *) malloc (sizeof (GLOBALROOT_PREFIX) + strlen (path));
if (ret)
stpcpy (stpcpy (ret, GLOBALROOT_PREFIX), path);
}
return ret;
}
@ -787,10 +835,12 @@ do_pathconv (char *filename)
tmp = buf;
if (strncmp (buf, "\\\\?\\", 4) == 0)
{
len = 0;
if (buf[5] == ':')
len = 4;
if (strncmp (buf + 4, "UNC\\", 4) == 0)
else if (!strncmp (buf + 4, "UNC\\", 4))
len = 6;
if (strlen (buf) < MAX_PATH + len)
if (len && strlen (buf) < MAX_PATH + len)
{
tmp += len;
if (len == 6)
@ -829,7 +879,7 @@ print_version ()
cygpath (cygwin) %.*s\n\
Path Conversion Utility\n\
Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, \n\
2007, 2008 Red Hat, Inc.\n\
2007, 2008, 2009, 2010 Red Hat, Inc.\n\
Compiled on %s\n\
", len, v, __DATE__);
}