Some fixes for RtlGetFullPathName_U(str):
- Start to polish RtlpCollapsePath (Work in progress)
- Correctly zero-out the path destination buffer

They fix the following tests:
* ntdll:RtlGetFullPathName_U (2 failures to full success)
* ntdll:RtlGetFullPathName_UstrEx (2 failures to full success)

svn path=/trunk/; revision=62623
This commit is contained in:
Hermès Bélusca-Maïto 2014-04-05 14:56:41 +00:00
parent 9ad09cfd87
commit e9cd63fb67

View file

@ -10,7 +10,7 @@
* Pierre Schweitzer (pierre@reactos.org) * Pierre Schweitzer (pierre@reactos.org)
*/ */
/* INCLUDES *****************************************************************/ /* INCLUDES *******************************************************************/
#include <rtl.h> #include <rtl.h>
@ -289,102 +289,137 @@ RtlpCheckDeviceName(IN PUNICODE_STRING FileName,
/****************************************************************** /******************************************************************
* RtlpCollapsePath (from WINE) * RtlpCollapsePath (from WINE)
* *
* Helper for RtlGetFullPathName_U. * Helper for RtlGetFullPathName_U
* 1) Convert slashes into backslashes *
* 2) Get rid of duplicate backslashes * 1) Converts slashes into backslashes and gets rid of duplicated ones;
* 3) Get rid of . and .. components in the path. * 2) Gets rid of . and .. components in the path.
*
* Returns the full path length without its terminating NULL character.
*/ */
static VOID static ULONG
RtlpCollapsePath(PWSTR path, ULONG mark, BOOLEAN SkipTrailingPathSeparators) RtlpCollapsePath(PWSTR Path, /* ULONG PathBufferSize, ULONG PathLength, */ ULONG mark, BOOLEAN SkipTrailingPathSeparators)
{ {
PWSTR p, next; PWSTR p, next;
/* convert every / into a \ */ // FIXME: Do not suppose NULL-terminated strings!!
for (p = path; *p; p++)
ULONG PathLength = wcslen(Path);
PWSTR EndBuffer = Path + PathLength; // Path + PathBufferSize / sizeof(WCHAR);
PWSTR EndPath;
/* Convert slashes into backslashes */
for (p = Path; *p; p++)
{ {
if (*p == L'/') *p = L'\\'; if (*p == L'/') *p = L'\\';
} }
/* collapse duplicate backslashes */ /* Collapse duplicate backslashes */
next = path + max( 1, mark ); next = Path + max( 1, mark );
for (p = next; *p; p++) for (p = next; *p; p++)
{ {
if (*p != L'\\' || next[-1] != L'\\') *next++ = *p; if (*p != L'\\' || next[-1] != L'\\') *next++ = *p;
} }
*next = UNICODE_NULL; *next = UNICODE_NULL;
EndPath = next;
p = path + mark; p = Path + mark;
while (*p) while (*p)
{ {
if (*p == L'.') if (*p == L'.')
{ {
switch(p[1]) switch (p[1])
{ {
case UNICODE_NULL: /* final . */ case UNICODE_NULL: /* final . */
if (p > path + mark) p--; if (p > Path + mark) p--;
*p = UNICODE_NULL; *p = UNICODE_NULL;
EndPath = p;
continue; continue;
case L'\\': /* .\ component */ case L'\\': /* .\ component */
next = p + 2; next = p + 2;
RtlMoveMemory( p, next, (wcslen(next) + 1) * sizeof(WCHAR) ); // ASSERT(EndPath - next == wcslen(next));
RtlMoveMemory(p, next, (EndPath - next + 1) * sizeof(WCHAR));
EndPath -= (next - p);
continue; continue;
case L'.': case L'.':
if (p[2] == L'\\') /* ..\ component */ if (p[2] == L'\\') /* ..\ component */
{ {
next = p + 3; next = p + 3;
if (p > path + mark) if (p > Path + mark)
{ {
p--; p--;
while (p > path + mark && p[-1] != L'\\') p--; while (p > Path + mark && p[-1] != L'\\') p--;
} }
RtlMoveMemory( p, next, (wcslen(next) + 1) * sizeof(WCHAR) ); // ASSERT(EndPath - next == wcslen(next));
RtlMoveMemory(p, next, (EndPath - next + 1) * sizeof(WCHAR));
EndPath -= (next - p);
continue; continue;
} }
else if (!p[2]) /* final .. */ else if (p[2] == UNICODE_NULL) /* final .. */
{ {
if (p > path + mark) if (p > Path + mark)
{ {
p--; p--;
while (p > path + mark && p[-1] != L'\\') p--; while (p > Path + mark && p[-1] != L'\\') p--;
if (p > path + mark) p--; if (p > Path + mark) p--;
} }
*p = UNICODE_NULL; *p = UNICODE_NULL;
EndPath = p;
continue; continue;
} }
break; break;
} }
} }
/* skip to the next component */
/* Skip to the next component */
while (*p && *p != L'\\') p++; while (*p && *p != L'\\') p++;
if (*p == L'\\') if (*p == L'\\')
{ {
/* remove last dot in previous dir name */ /* Remove last dot in previous dir name */
if (p > path + mark && p[-1] == L'.') if (p > Path + mark && p[-1] == L'.')
RtlMoveMemory( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) ); {
// ASSERT(EndPath - p == wcslen(p));
RtlMoveMemory(p - 1, p, (EndPath - p + 1) * sizeof(WCHAR));
EndPath--;
}
else else
{
p++; p++;
}
} }
} }
/* Remove trailing backslashes if needed (after the UNC part if it exists) */ /* Remove trailing backslashes if needed (after the UNC part if it exists) */
if (SkipTrailingPathSeparators) if (SkipTrailingPathSeparators)
{ {
while (p > path + mark && IS_PATH_SEPARATOR(p[-1])) *p-- = UNICODE_NULL; while (p > Path + mark && IS_PATH_SEPARATOR(p[-1])) p--;
} }
/* Remove trailing spaces and dots (for all the path) */ /* Remove trailing spaces and dots (for all the path) */
while (p > path && (p[-1] == L' ' || p[-1] == L'.')) *p-- = UNICODE_NULL; while (p > Path && (p[-1] == L' ' || p[-1] == L'.')) p--;
/* Null-terminate the string */ /*
*p = UNICODE_NULL; * Zero-out the discarded buffer zone, starting just after
* the path string and going up to the end of the buffer.
* It also NULL-terminate the path string.
*/
ASSERT(EndBuffer >= p);
RtlZeroMemory(p, (EndBuffer - p + 1) * sizeof(WCHAR));
/* Return the real path length */
PathLength = (p - Path);
// ASSERT(PathLength == wcslen(Path));
return (PathLength * sizeof(WCHAR));
} }
/****************************************************************** /******************************************************************
* RtlpSkipUNCPrefix (from WINE) * RtlpSkipUNCPrefix (from WINE)
* *
* Helper for RtlGetFullPathName_U * Helper for RtlGetFullPathName_U
* Skip the \\share\dir part of a file name and return the new position *
* (which can point on the last backslash of "dir\") * Skips the \\share\dir part of a file name and returns the new position
* (which can point on the last backslash of "dir\").
*/ */
static SIZE_T static SIZE_T
RtlpSkipUNCPrefix(PCWSTR FileNameBuffer) RtlpSkipUNCPrefix(PCWSTR FileNameBuffer)
@ -542,7 +577,7 @@ RtlGetFullPathName_Ustr(
return FullLength + sizeof(UNICODE_NULL); return FullLength + sizeof(UNICODE_NULL);
} }
/* Zero out the destination buffer. FileName must be different from Buffer */ /* Zero-out the destination buffer. FileName must be different from Buffer */
RtlZeroMemory(Buffer, Size); RtlZeroMemory(Buffer, Size);
/* Get the path type */ /* Get the path type */
@ -551,7 +586,7 @@ RtlGetFullPathName_Ustr(
/********************************************** /**********************************************
** CODE REWRITTEN IS HAPPENING THERE ** ** CODE REWRITING IS HAPPENING THERE **
**********************************************/ **********************************************/
Source = FileNameBuffer; Source = FileNameBuffer;
SourceLength = FileNameLength; SourceLength = FileNameLength;
@ -619,7 +654,7 @@ RtlGetFullPathName_Ustr(
* FIXME: Win2k seems to check that the environment * FIXME: Win2k seems to check that the environment
* variable actually points to an existing directory. * variable actually points to an existing directory.
* If not, root of the drive is used (this seems also * If not, root of the drive is used (this seems also
* to be the only spot in RtlGetFullPathName that the * to be the only place in RtlGetFullPathName that the
* existence of a part of a path is checked). * existence of a part of a path is checked).
*/ */
EnvVarValue.Buffer[EnvVarValue.Length / sizeof(WCHAR)] = L'\\'; EnvVarValue.Buffer[EnvVarValue.Length / sizeof(WCHAR)] = L'\\';
@ -709,13 +744,10 @@ RtlGetFullPathName_Ustr(
/* /*
* Build the full path * Build the full path
*/ */
// if (ShortName) DPRINT1("buffer(1) = %S\n", Buffer);
/* Copy the path's prefix */ /* Copy the path's prefix */
if (PrefixLength) RtlMoveMemory(Buffer, Prefix, PrefixLength); if (PrefixLength) RtlMoveMemory(Buffer, Prefix, PrefixLength);
// if (ShortName) DPRINT1("buffer(2) = %S\n", Buffer);
/* Copy the remaining part of the path */ /* Copy the remaining part of the path */
RtlMoveMemory(Buffer + PrefixLength / sizeof(WCHAR), Source, SourceLength + sizeof(WCHAR)); RtlMoveMemory(Buffer + PrefixLength / sizeof(WCHAR), Source, SourceLength + sizeof(WCHAR));
// if (ShortName) DPRINT1("buffer(3) = %S\n", Buffer);
/* Some cleanup */ /* Some cleanup */
Prefix = NULL; Prefix = NULL;
@ -726,15 +758,11 @@ RtlGetFullPathName_Ustr(
} }
/* /*
* Finally, put the path in canonical form, * Finally, put the path in canonical form (remove redundant . and ..,
* i.e. simplify redundant . and .., etc... * (back)slashes...) and retrieve the length of the full path name
* (without its terminating null character) (in chars).
*/ */
// if (*PathType == RtlPathTypeUncAbsolute) DPRINT1("RtlpCollapsePath('%S', %lu)\n", Buffer, PrefixCut); reqsize = RtlpCollapsePath(Buffer, /* Size, reqsize, */ PrefixCut, SkipTrailingPathSeparators);
RtlpCollapsePath(Buffer, PrefixCut, SkipTrailingPathSeparators);
// if (ShortName) DPRINT1("buffer(4) = %S\n", Buffer);
/* Get the length of the full path name, without its terminating null character */
reqsize = wcslen(Buffer) * sizeof(WCHAR);
/* Find the file part, which is present after the last path separator */ /* Find the file part, which is present after the last path separator */
if (ShortName) if (ShortName)
@ -761,7 +789,8 @@ RtlGetFullPathName_Ustr(
Quit: Quit:
/* Release PEB lock */ /* Release PEB lock */
RtlReleasePebLock(); RtlReleasePebLock();
return (ULONG)reqsize;
return reqsize;
} }
NTSTATUS NTSTATUS