* 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> 2010-08-28 Corinna Vinschen <corinna@vinschen.de>
* ldh.cc (WinMain): Change DONT_RESOLVE_DLL_REFERENCES to * ldh.cc (WinMain): Change DONT_RESOLVE_DLL_REFERENCES to

View File

@ -134,11 +134,23 @@ static inline BOOLEAN
RtlAllocateUnicodeString (PUNICODE_STRING uni, ULONG size) RtlAllocateUnicodeString (PUNICODE_STRING uni, ULONG size)
{ {
uni->Length = 0; uni->Length = 0;
uni->MaximumLength = 512; uni->MaximumLength = size / sizeof (WCHAR);
uni->Buffer = (WCHAR *) malloc (size); uni->Buffer = (WCHAR *) malloc (size);
return uni->Buffer != NULL; 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 static size_t
my_wcstombs (char *dest, const wchar_t *src, size_t n) 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); return wcstombs (dest, src, n);
} }
#define HARDDISK_PREFIX L"\\Device\\Harddisk"
#define GLOBALROOT_PREFIX "\\\\.\\GLOBALROOT"
static char * static char *
get_device_name (char *path) get_device_name (char *path)
{ {
@ -156,18 +171,21 @@ get_device_name (char *path)
OBJECT_ATTRIBUTES ntobj; OBJECT_ATTRIBUTES ntobj;
NTSTATUS status; NTSTATUS status;
HANDLE lnk, dir; HANDLE lnk, dir;
bool got_one = false;
char *ret = strdup (path); char *ret = strdup (path);
PDIRECTORY_BASIC_INFORMATION odi = (PDIRECTORY_BASIC_INFORMATION) PDIRECTORY_BASIC_INFORMATION odi = (PDIRECTORY_BASIC_INFORMATION)
alloca (4096); alloca (4096);
BOOLEAN restart; BOOLEAN restart;
ULONG cont; ULONG cont;
if (!strncasecmp (path, GLOBALROOT_PREFIX "\\", sizeof (GLOBALROOT_PREFIX)))
path += sizeof (GLOBALROOT_PREFIX) - 1;
if (strncasecmp (path, "\\Device\\", 8)) if (strncasecmp (path, "\\Device\\", 8))
return ret; return ret;
if (!RtlAllocateUnicodeString (&ntdev, 65536)) if (!RtlAllocateUnicodeString (&ntdev, 65534))
return ret; return ret;
if (!RtlAllocateUnicodeString (&tgtdev, 65536)) if (!RtlAllocateUnicodeString (&tgtdev, 65534))
return ret; return ret;
RtlInitAnsiString (&ans, path); RtlInitAnsiString (&ans, path);
RtlAnsiStringToUnicodeString (&ntdev, &ans, FALSE); RtlAnsiStringToUnicodeString (&ntdev, &ans, FALSE);
@ -185,7 +203,8 @@ get_device_name (char *path)
goto out; goto out;
RtlCopyUnicodeString (&ntdev, &tgtdev); 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; goto out;
for (int i = 0; i < 2; ++i) for (int i = 0; i < 2; ++i)
@ -221,12 +240,19 @@ get_device_name (char *path)
ZwClose (lnk); ZwClose (lnk);
if (!NT_SUCCESS (status)) if (!NT_SUCCESS (status))
continue; 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 /* If the comparison succeeds, the name of the directory entry is
a valid DOS device name, if prepended with "\\.\". Return that a valid DOS device name, if prepended with "\\.\". Return that
valid DOS path. */ valid DOS path. */
wchar_t *trailing = NULL;
if (ntdev.Length > tgtdev.Length)
trailing = ntdev.Buffer + tgtdev.Length / sizeof (WCHAR);
ULONG len = RtlUnicodeStringToAnsiSize (&odi->ObjectName); ULONG len = RtlUnicodeStringToAnsiSize (&odi->ObjectName);
if (trailing)
len += my_wcstombs (NULL, trailing, 0);
free (ret); free (ret);
ret = (char *) malloc (len + 4); ret = (char *) malloc (len + 4);
strcpy (ret, "\\\\.\\"); strcpy (ret, "\\\\.\\");
@ -234,15 +260,30 @@ get_device_name (char *path)
ans.MaximumLength = len; ans.MaximumLength = len;
ans.Buffer = ret + 4; ans.Buffer = ret + 4;
RtlUnicodeStringToAnsiString (&ans, &odi->ObjectName, FALSE); 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 /* Special case for local disks: It's most feasible if the
DOS device name reflects the DOS drive, so we check for this DOS device name reflects the DOS drive, so we check for this
explicitly and only return prematurely if so. */ explicitly and only return prematurely if so. */
#define HARDDISK_PREFIX L"\\Device\\Harddisk"
if (ntdev.Length < wcslen (HARDDISK_PREFIX) if (ntdev.Length < wcslen (HARDDISK_PREFIX)
|| wcsncasecmp (ntdev.Buffer, HARDDISK_PREFIX, 8) != 0 || wcsncasecmp (ntdev.Buffer, HARDDISK_PREFIX, 8) != 0
|| (odi->ObjectName.Length == 2 * sizeof (WCHAR) || (odi->ObjectName.Length == 2 * sizeof (WCHAR)
&& odi->ObjectName.Buffer[1] == L':')) && 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); ZwClose (dir);
goto out; goto out;
} }
@ -254,6 +295,13 @@ get_device_name (char *path)
out: out:
free (tgtdev.Buffer); free (tgtdev.Buffer);
free (ntdev.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; return ret;
} }
@ -787,10 +835,12 @@ do_pathconv (char *filename)
tmp = buf; tmp = buf;
if (strncmp (buf, "\\\\?\\", 4) == 0) if (strncmp (buf, "\\\\?\\", 4) == 0)
{ {
len = 4; len = 0;
if (strncmp (buf + 4, "UNC\\", 4) == 0) if (buf[5] == ':')
len = 4;
else if (!strncmp (buf + 4, "UNC\\", 4))
len = 6; len = 6;
if (strlen (buf) < MAX_PATH + len) if (len && strlen (buf) < MAX_PATH + len)
{ {
tmp += len; tmp += len;
if (len == 6) if (len == 6)
@ -829,7 +879,7 @@ print_version ()
cygpath (cygwin) %.*s\n\ cygpath (cygwin) %.*s\n\
Path Conversion Utility\n\ Path Conversion Utility\n\
Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, \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\ Compiled on %s\n\
", len, v, __DATE__); ", len, v, __DATE__);
} }