[KERNEL32]: Rewrite GetLong/ShortPathNameW away from Wine. Some changes:

- Windows allows the two input buffers to overlap. This means we have to be very careful with copying (and always use RtlMoveMemory) -- the old function was not handling this at all.
   - We also have to handle cases where we need to make our own local buffer copy.
   - Length validation is more stringent now.
   - Checking for short/long path names wasn't correct w.r.t ANSI file mode, as it was calling Rtl which assumes OEM.
   - Shortcuts were taken while parsing slashes and path separators. We now call into Rtl to support this, and also support unlimited slashes (note that \\??\c:\\\windows\\\\system32 is actully a valid path, for example).
   - ErrorMode is now correctly set for the thread, to avoid "Insert floppy" or "Close CDROM bay door" errors as we are using the FindFile API.
   - Correct LastError is set where appropriate.
   - An application compatibility flag is now supported.

svn path=/trunk/; revision=54318
This commit is contained in:
Alex Ionescu 2011-11-06 17:17:16 +00:00
parent 61db405237
commit 1bdbaed2a7

View file

@ -18,8 +18,242 @@
UNICODE_STRING BaseDllDirectory; UNICODE_STRING BaseDllDirectory;
UNICODE_STRING NoDefaultCurrentDirectoryInExePath = RTL_CONSTANT_STRING(L"NoDefaultCurrentDirectoryInExePath"); UNICODE_STRING NoDefaultCurrentDirectoryInExePath = RTL_CONSTANT_STRING(L"NoDefaultCurrentDirectoryInExePath");
/* This is bitmask for each illegal filename character */
/* If someone has time, please feel free to use 0b notation */
DWORD IllegalMask[4] =
{
0xFFFFFFFF, // None allowed (00 to 1F)
0xFC009C05, // 20, 22, 2A, 2B, 2C, 2F, 3A, 3B, 3C, 3D, 3E, 3F not allowed
0x38000000, // 5B, 5C, 5D not allowed
0x10000000 // 7C not allowed
};
/* FUNCTIONS ******************************************************************/ /* FUNCTIONS ******************************************************************/
/*
* Why not use RtlIsNameLegalDOS8Dot3? In fact the actual algorithm body is
* identical (other than the Rtl can optionally check for spaces), however the
* Rtl will always convert to OEM, while kernel32 has two possible file modes
* (ANSI or OEM). Therefore we must duplicate the algorithm body to get
* the correct compatible results
*/
BOOL
WINAPI
IsShortName_U(IN PWCHAR Name,
IN ULONG Length)
{
BOOLEAN HasExtension;
WCHAR c;
NTSTATUS Status;
UNICODE_STRING UnicodeName;
ANSI_STRING AnsiName;
ULONG i, Dots;
CHAR AnsiBuffer[MAX_PATH];
ASSERT(Name);
/* What do you think 8.3 means? */
if (Length > 12) return FALSE;
/* Sure, any emtpy name is a short name */
if (!Length) return TRUE;
/* This could be . or .. or somethign else */
if (*Name == L'.')
{
/* Which one is it */
if ((Length == 1) || ((Length == 2) && *(Name + 1) == L'.'))
{
/* . or .., this is good */
return TRUE;
}
/* Some other bizare dot-based name, not good */
return FALSE;
}
/* Initialize our two strings */
RtlInitEmptyAnsiString(&AnsiName, AnsiBuffer, MAX_PATH);
RtlInitEmptyUnicodeString(&UnicodeName, Name, Length * sizeof(WCHAR));
UnicodeName.Length = UnicodeName.MaximumLength;
/* Now do the conversion */
Status = BasepUnicodeStringTo8BitString(&AnsiName, &UnicodeName, FALSE);
if (!NT_SUCCESS(Status)) return FALSE;
/* Now we loop the name */
HasExtension = FALSE;
for (i = 0, Dots = Length - 1; i < AnsiName.Length; i++, Dots--)
{
/* Read the current byte */
c = AnsiName.Buffer[i];
/* Is it DBCS? */
if (IsDBCSLeadByte(c))
{
/* If we're near the end of the string, we can't allow a DBCS */
if ((!(HasExtension) && (i >= 7)) || (i == AnsiName.Length - 1))
{
return FALSE;
}
/* Otherwise we skip over it */
continue;
}
/* Check for illegal characters */
if ((c > 0x7F) || (IllegalMask[c / 32] && (1 << (c % 32))))
{
return FALSE;
}
/* Check if this is perhaps an extension? */
if (c == L'.')
{
/* Unless the extension is too large or there's more than one */
if ((HasExtension) || (Dots > 3)) return FALSE;
/* This looks like an extension */
HasExtension = TRUE;
}
/* 8.3 length was validated, but now we must guard against 9.2 or similar */
if ((i >= 8) && !(HasExtension)) return FALSE;
}
/* You survived the loop, this is a good short name */
return TRUE;
}
BOOL
WINAPI
IsLongName_U(IN PWCHAR FileName,
IN ULONG Length)
{
BOOLEAN HasExtension;
ULONG i, Dots;
/* More than 8.3, any combination of dots, and NULL names are all long */
if (!(Length) || (Length > 12) || (*FileName == L'.')) return TRUE;
/* Otherwise, initialize our scanning loop */
HasExtension = FALSE;
for (i = 0, Dots = Length - 1; i < Length; i++, Dots--)
{
/* Check if this could be an extension */
if (*FileName == '.')
{
/* Unlike the short case, we WANT more than one extension, or a long one */
if ((HasExtension) || (Dots > 3)) return TRUE;
HasExtension = TRUE;
}
/* Check if this would violate the "8" in 8.3, ie. 9.2 */
if ((i >= 8) && (!HasExtension)) return TRUE;
}
/* The name *seems* to conform to 8.3 */
return FALSE;
}
BOOL
WINAPI
FindLFNorSFN_U(IN PWCHAR Path,
OUT PWCHAR *First,
OUT PWCHAR *Last,
IN BOOL UseShort)
{
PWCHAR p;
ULONG Length;
BOOL Found = 0;
ASSERT(Path);
/* Loop while there is something in the path */
while (TRUE)
{
/* Loop within the path skipping slashes */
while ((*Path) && ((*Path == L'\\') || (*Path == L'/'))) Path++;
/* Make sure there's something after the slashes too! */
if (*Path == UNICODE_NULL) break;
/* Now do the same thing with the last marker */
p = Path + 1;
while ((*p) && ((*p == L'\\') || (*p == L'/'))) p++;
/* Whatever is in between those two is now the file name length */
Length = p - Path;
/*
* Check if it is valid
* Note that !IsShortName != IsLongName, these two functions simply help
* us determine if a conversion is necessary or not.
*/
Found = UseShort ? IsShortName_U(Path, Length) : IsLongName_U(Path, Length);
/* "Found" really means: "Is a conversion necessary?", hence the ! */
if (!Found)
{
/* It is! did the caller request to know the markers? */
if ((First) && (Last))
{
/* Return them */
*First = Path;
*Last = p;
}
break;
}
/* Is there anything else following this sub-path/filename? */
if (*p == UNICODE_NULL) break;
/* Yes, keep going */
Path = p + 1;
}
/* Return if anything was found and valid */
return !Found;
}
PWCHAR
WINAPI
SkipPathTypeIndicator_U(IN PWCHAR Path)
{
PWCHAR ReturnPath;
ULONG i;
/* Check what kind of path this is and how many slashes to skip */
switch (RtlDetermineDosPathNameType_U(Path))
{
case RtlPathTypeDriveAbsolute:
return Path + 3;
case RtlPathTypeDriveRelative:
return Path + 2;
case RtlPathTypeRooted:
return Path + 1;
case RtlPathTypeRelative:
return Path;
case RtlPathTypeRootLocalDevice:
default:
return NULL;
case RtlPathTypeUncAbsolute:
case RtlPathTypeLocalDevice:
/* Keep going until we bypass the path indicators */
for (ReturnPath = Path + 2, i = 2; (i > 0) && (*ReturnPath); ReturnPath++)
{
/* We look for 2 slashes, so keep at it until we find them */
if ((*ReturnPath == L'\\') || (*ReturnPath == L'/')) i--;
}
return ReturnPath;
}
}
BOOL BOOL
WINAPI WINAPI
BasepIsCurDirAllowedForPlainExeNames(VOID) BasepIsCurDirAllowedForPlainExeNames(VOID)
@ -536,108 +770,221 @@ SearchPathW(LPCWSTR lpPath,
return ret; return ret;
} }
/*********************************************************************** /*
* @implemented * @implemented
*
* GetLongPathNameW (KERNEL32.@)
*
* NOTES
* observed (Win2000):
* shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
* shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
*/ */
DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen ) DWORD
WINAPI
GetLongPathNameW(IN LPCWSTR lpszShortPath,
IN LPWSTR lpszLongPath,
IN DWORD cchBuffer)
{ {
#define MAX_PATHNAME_LEN 1024 PWCHAR Path, Original, First, Last, Buffer, Src, Dst;
ULONG Length;
WCHAR LastChar;
HANDLE FindHandle;
DWORD ReturnLength;
ULONG ErrorMode;
BOOLEAN Found;
WIN32_FIND_DATAW FindFileData;
WCHAR tmplongpath[MAX_PATHNAME_LEN]; /* Initialize so Quickie knows there's nothing to do */
LPCWSTR p; Buffer = Original = NULL;
DWORD sp = 0, lp = 0; ReturnLength = 0;
DWORD tmplen;
BOOL unixabsolute;
WIN32_FIND_DATAW wfd;
HANDLE goit;
if (!shortpath) /* First check if the input path was obviously NULL */
if (!lpszShortPath)
{ {
/* Fail the request */
SetLastError(ERROR_INVALID_PARAMETER); SetLastError(ERROR_INVALID_PARAMETER);
return 0; return 0;
} }
if (!shortpath[0])
/* We will be touching removed, removable drives -- don't warn the user */
ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
/* Do a simple check to see if the path exists */
if (GetFileAttributesW(lpszShortPath) == 0xFFFFFFF)
{ {
SetLastError(ERROR_PATH_NOT_FOUND); /* It doesn't, so fail */
return 0; ReturnLength = 0;
goto Quickie;
} }
DPRINT("GetLongPathNameW(%s,%p,%ld)\n", shortpath, longpath, longlen); /* Now get a pointer to the actual path, skipping indicators */
Path = SkipPathTypeIndicator_U((PWCHAR)lpszShortPath);
if (shortpath[0] == '\\' && shortpath[1] == '\\') /* Try to find a file name in there */
{ if (Path) Found = FindLFNorSFN_U(Path, &First, &Last, FALSE);
DPRINT1("ERR: UNC pathname %s\n", shortpath);
lstrcpynW( longpath, shortpath, longlen );
return wcslen(longpath);
}
unixabsolute = (shortpath[0] == '/');
/* check for drive letter */
if (!unixabsolute && shortpath[1] == ':' )
{
tmplongpath[0] = shortpath[0];
tmplongpath[1] = ':';
lp = sp = 2;
}
while (shortpath[sp]) /* Is there any path or filename in there? */
if (!(Path) || (*Path == UNICODE_NULL) || !(Found))
{ {
/* check for path delimiters and reproduce them */ /* There isn't, so the long path is simply the short path */
if (shortpath[sp] == '\\' || shortpath[sp] == '/') ReturnLength = wcslen(lpszShortPath);
/* Is there space for it? */
if ((cchBuffer > ReturnLength) && (lpszLongPath))
{ {
if (!lp || tmplongpath[lp-1] != '\\') /* Make sure the pointers aren't already the same */
if (lpszLongPath != lpszShortPath)
{ {
/* strip double "\\" */ /* They're not -- copy the short path into the long path */
tmplongpath[lp++] = '\\'; RtlMoveMemory(lpszLongPath,
lpszShortPath,
ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
} }
tmplongpath[lp] = 0; /* terminate string */
sp++;
continue;
} }
else
p = shortpath + sp;
if (sp == 0 && p[0] == '.' && (p[1] == '/' || p[1] == '\\'))
{ {
tmplongpath[lp++] = *p++; /* Otherwise, let caller know we need a bigger buffer, include NULL */
tmplongpath[lp++] = *p++; ReturnLength++;
} }
for (; *p && *p != '/' && *p != '\\'; p++); goto Quickie;
tmplen = p - (shortpath + sp);
lstrcpynW(tmplongpath + lp, shortpath + sp, tmplen + 1);
/* Check if the file exists and use the existing file name */
goit = FindFirstFileW(tmplongpath, &wfd);
if (goit == INVALID_HANDLE_VALUE)
{
DPRINT("not found %s!\n", tmplongpath);
SetLastError ( ERROR_FILE_NOT_FOUND );
return 0;
}
FindClose(goit);
wcscpy(tmplongpath + lp, wfd.cFileName);
lp += wcslen(tmplongpath + lp);
sp += tmplen;
} }
tmplen = wcslen(shortpath) - 1;
if ((shortpath[tmplen] == '/' || shortpath[tmplen] == '\\') &&
(tmplongpath[lp - 1] != '/' && tmplongpath[lp - 1] != '\\'))
tmplongpath[lp++] = shortpath[tmplen];
tmplongpath[lp] = 0;
tmplen = wcslen(tmplongpath) + 1; /* We are still in the game -- compute the current size */
if (tmplen <= longlen) Length = wcslen(lpszShortPath) + sizeof(ANSI_NULL);
Original = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR));
if (!Original) goto ErrorQuickie;
/* Make a copy ofi t */
RtlMoveMemory(Original, lpszShortPath, Length * sizeof(WCHAR));
/* Compute the new first and last markers */
First = &Original[First - lpszShortPath];
Last = &Original[Last - lpszShortPath];
/* Set the current destination pointer for a copy */
Dst = lpszLongPath;
/*
* Windows allows the paths to overlap -- we have to be careful with this and
* see if it's same to do so, and if not, allocate our own internal buffer
* that we'll return at the end.
*
* This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
*/
if ((cchBuffer) && (lpszLongPath) &&
(((lpszLongPath >= lpszShortPath) && (lpszLongPath < &lpszShortPath[Length])) ||
((lpszLongPath < lpszShortPath) && (&lpszLongPath[cchBuffer] >= lpszShortPath))))
{ {
wcscpy(longpath, tmplongpath); Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR));
DPRINT("returning %s\n", longpath); if (!Buffer) goto ErrorQuickie;
tmplen--; /* length without 0 */
/* New destination */
Dst = Buffer;
} }
return tmplen; /* Prepare for the loop */
Src = Original;
ReturnLength = 0;
while (TRUE)
{
/* Current delta in the loop */
Length = First - Src;
/* Update the return length by it */
ReturnLength += Length;
/* Is there a delta? If so, is there space and buffer for it? */
if ((Length) && (cchBuffer > ReturnLength) && (lpszLongPath))
{
RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR));
Dst += Length;
}
/* "Terminate" this portion of the path's substring so we can do a find */
LastChar = *Last;
*Last = UNICODE_NULL;
FindHandle = FindFirstFileW(Original, &FindFileData);
*Last = LastChar;
/* This portion wasn't found, so fail */
if (FindHandle == INVALID_HANDLE_VALUE)
{
ReturnLength = 0;
break;
}
/* Close the find handle */
FindClose(FindHandle);
/* Now check the length of the long name */
Length = wcslen(FindFileData.cFileName);
if (Length)
{
/* This is our new first marker */
First = FindFileData.cFileName;
}
else
{
/* Otherwise, the name is the delta between our current markers */
Length = Last - First;
}
/* Update the return length with the short name length, if any */
ReturnLength += Length;
/* Once again check for appropriate space and buffer */
if ((cchBuffer > ReturnLength) && (lpszLongPath))
{
/* And do the copy if there is */
RtlMoveMemory(Dst, First, Length * sizeof(WCHAR));
Dst += Length;
}
/* Now update the source pointer */
Src = Last;
if (*Src == UNICODE_NULL) break;
/* Are there more names in there? */
Found = FindLFNorSFN_U(Src, &First, &Last, FALSE);
if (!Found) break;
}
/* The loop is done, is there anything left? */
if (ReturnLength)
{
/* Get the length of the straggling path */
Length = wcslen(Src);
ReturnLength += Length;
/* Once again check for appropriate space and buffer */
if ((cchBuffer > ReturnLength) && (lpszLongPath))
{
/* And do the copy if there is -- accounting for NULL here */
RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR) + sizeof(UNICODE_NULL));
/* What about our buffer? */
if (Buffer)
{
/* Copy it into the caller's long path */
RtlMoveMemory(lpszLongPath,
Buffer,
ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
}
}
else
{
/* Buffer is too small, let the caller know, making space for NULL */
ReturnLength++;
}
}
/* We're all done */
goto Quickie;
ErrorQuickie:
/* This is the goto for memory failures */
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
Quickie:
/* General function end: free memory, restore error mode, return length */
if (Original) RtlFreeHeap(RtlGetProcessHeap(), 0, Original);
if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
SetErrorMode(ErrorMode);
return ReturnLength;
} }
/* /*
@ -660,13 +1007,13 @@ GetLongPathNameA(IN LPCSTR lpszShortPath,
LongPathAnsi.Buffer = NULL; LongPathAnsi.Buffer = NULL;
ShortPathUni.Buffer = NULL; ShortPathUni.Buffer = NULL;
Result = 0; Result = 0;
if (!lpszShortPath) if (!lpszShortPath)
{ {
SetLastError(ERROR_INVALID_PARAMETER); SetLastError(ERROR_INVALID_PARAMETER);
return 0; return 0;
} }
Status = Basep8BitStringToDynamicUnicodeString(&ShortPathUni, lpszShortPath); Status = Basep8BitStringToDynamicUnicodeString(&ShortPathUni, lpszShortPath);
if (!NT_SUCCESS(Status)) goto Quickie; if (!NT_SUCCESS(Status)) goto Quickie;
@ -686,7 +1033,7 @@ GetLongPathNameA(IN LPCSTR lpszShortPath,
PathLength = GetLongPathNameW(ShortPathUni.Buffer, LongPath, PathLength); PathLength = GetLongPathNameW(ShortPathUni.Buffer, LongPath, PathLength);
} }
} }
if (!PathLength) goto Quickie; if (!PathLength) goto Quickie;
ShortPathUni.MaximumLength = PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL); ShortPathUni.MaximumLength = PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
@ -741,13 +1088,13 @@ GetShortPathNameA(IN LPCSTR lpszLongPath,
ShortPathAnsi.Buffer = NULL; ShortPathAnsi.Buffer = NULL;
LongPathUni.Buffer = NULL; LongPathUni.Buffer = NULL;
Result = 0; Result = 0;
if (!lpszLongPath) if (!lpszLongPath)
{ {
SetLastError(ERROR_INVALID_PARAMETER); SetLastError(ERROR_INVALID_PARAMETER);
return 0; return 0;
} }
Status = Basep8BitStringToDynamicUnicodeString(&LongPathUni, lpszLongPath); Status = Basep8BitStringToDynamicUnicodeString(&LongPathUni, lpszLongPath);
if (!NT_SUCCESS(Status)) goto Quickie; if (!NT_SUCCESS(Status)) goto Quickie;
@ -767,7 +1114,7 @@ GetShortPathNameA(IN LPCSTR lpszLongPath,
PathLength = GetLongPathNameW(LongPathUni.Buffer, ShortPath, PathLength); PathLength = GetLongPathNameW(LongPathUni.Buffer, ShortPath, PathLength);
} }
} }
if (!PathLength) goto Quickie; if (!PathLength) goto Quickie;
LongPathUni.MaximumLength = PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL); LongPathUni.MaximumLength = PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
@ -802,109 +1149,225 @@ Quickie:
return Result; return Result;
} }
/* /*
* NOTE: Copied from Wine.
* @implemented * @implemented
*/ */
DWORD DWORD
WINAPI WINAPI
GetShortPathNameW ( GetShortPathNameW(IN LPCWSTR lpszLongPath,
LPCWSTR longpath, IN LPWSTR lpszShortPath,
LPWSTR shortpath, IN DWORD cchBuffer)
DWORD shortlen
)
{ {
WCHAR tmpshortpath[MAX_PATH]; PWCHAR Path, Original, First, Last, Buffer, Src, Dst;
LPCWSTR p; ULONG Length;
DWORD sp = 0, lp = 0; WCHAR LastChar;
DWORD tmplen; HANDLE FindHandle;
WIN32_FIND_DATAW wfd; DWORD ReturnLength;
HANDLE goit; ULONG ErrorMode;
UNICODE_STRING ustr; BOOLEAN Found;
WCHAR ustr_buf[8+1+3+1]; WIN32_FIND_DATAW FindFileData;
DPRINT("GetShortPathNameW: %S\n",longpath); /* Initialize so Quickie knows there's nothing to do */
Buffer = Original = NULL;
ReturnLength = 0;
if (!longpath) /* First check if the input path was obviously NULL */
if (!lpszLongPath)
{ {
/* Fail the request */
SetLastError(ERROR_INVALID_PARAMETER); SetLastError(ERROR_INVALID_PARAMETER);
return 0; return 0;
} }
if (!longpath[0])
{
SetLastError(ERROR_BAD_PATHNAME);
return 0;
}
/* check for drive letter */ /* We will be touching removed, removable drives -- don't warn the user */
if (longpath[0] != '/' && longpath[1] == ':' ) ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
{
tmpshortpath[0] = longpath[0];
tmpshortpath[1] = ':';
sp = lp = 2;
}
ustr.Buffer = ustr_buf; /* Do a simple check to see if the path exists */
ustr.Length = 0; if (GetFileAttributesW(lpszShortPath) == 0xFFFFFFF)
ustr.MaximumLength = sizeof(ustr_buf);
while (longpath[lp])
{ {
/* check for path delimiters and reproduce them */ /* Windows checks for an application compatibility flag to allow this */
if (longpath[lp] == '\\' || longpath[lp] == '/') if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags.LowPart & 1))
{ {
if (!sp || tmpshortpath[sp-1] != '\\') /* It doesn't, so fail */
{ ReturnLength = 0;
/* strip double "\\" */ goto Quickie;
tmpshortpath[sp] = '\\';
sp++;
}
tmpshortpath[sp] = 0; /* terminate string */
lp++;
continue;
} }
}
for (p = longpath + lp; *p && *p != '/' && *p != '\\'; p++); /* Now get a pointer to the actual path, skipping indicators */
tmplen = p - (longpath + lp); Path = SkipPathTypeIndicator_U((PWCHAR)lpszShortPath);
lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
/* Check, if the current element is a valid dos name */ /* Try to find a file name in there */
if (tmplen <= 8+1+3) if (Path) Found = FindLFNorSFN_U(Path, &First, &Last, TRUE);
/* Is there any path or filename in there? */
if (!(Path) || (*Path == UNICODE_NULL) || !(Found))
{
/* There isn't, so the long path is simply the short path */
ReturnLength = wcslen(lpszLongPath);
/* Is there space for it? */
if ((cchBuffer > ReturnLength) && (lpszLongPath))
{ {
BOOLEAN spaces; /* Make sure the pointers aren't already the same */
memcpy(ustr_buf, longpath + lp, tmplen * sizeof(WCHAR)); if (lpszLongPath != lpszShortPath)
ustr_buf[tmplen] = '\0';
ustr.Length = (USHORT)tmplen * sizeof(WCHAR);
if (RtlIsNameLegalDOS8Dot3(&ustr, NULL, &spaces) && !spaces)
{ {
sp += tmplen; /* They're not -- copy the short path into the long path */
lp += tmplen; RtlMoveMemory(lpszShortPath,
continue; lpszLongPath,
ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
} }
} }
else
/* Check if the file exists and use the existing short file name */ {
goit = FindFirstFileW(tmpshortpath, &wfd); /* Otherwise, let caller know we need a bigger buffer, include NULL */
if (goit == INVALID_HANDLE_VALUE) goto notfound; ReturnLength++;
FindClose(goit); }
lstrcpyW(tmpshortpath + sp, wfd.cAlternateFileName); goto Quickie;
sp += lstrlenW(tmpshortpath + sp);
lp += tmplen;
} }
tmpshortpath[sp] = 0;
tmplen = lstrlenW(tmpshortpath) + 1; /* We are still in the game -- compute the current size */
if (tmplen <= shortlen) Length = wcslen(lpszLongPath) + sizeof(ANSI_NULL);
Original = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR));
if (!Original) goto ErrorQuickie;
/* Make a copy of it */
wcsncpy(Original, lpszLongPath, Length);
/* Compute the new first and last markers */
First = &Original[First - lpszLongPath];
Last = &Original[Last - lpszLongPath];
/* Set the current destination pointer for a copy */
Dst = lpszShortPath;
/*
* Windows allows the paths to overlap -- we have to be careful with this and
* see if it's same to do so, and if not, allocate our own internal buffer
* that we'll return at the end.
*
* This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
*/
if ((cchBuffer) && (lpszShortPath) &&
(((lpszShortPath >= lpszLongPath) && (lpszShortPath < &lpszLongPath[Length])) ||
((lpszShortPath < lpszLongPath) && (&lpszShortPath[cchBuffer] >= lpszLongPath))))
{ {
lstrcpyW(shortpath, tmpshortpath); Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR));
tmplen--; /* length without 0 */ if (!Buffer) goto ErrorQuickie;
/* New destination */
Dst = Buffer;
} }
return tmplen; /* Prepare for the loop */
Src = Original;
ReturnLength = 0;
while (TRUE)
{
/* Current delta in the loop */
Length = First - Src;
notfound: /* Update the return length by it */
SetLastError ( ERROR_FILE_NOT_FOUND ); ReturnLength += Length;
return 0;
/* Is there a delta? If so, is there space and buffer for it? */
if ((Length) && (cchBuffer > ReturnLength) && (lpszShortPath))
{
RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR));
Dst += Length;
}
/* "Terminate" this portion of the path's substring so we can do a find */
LastChar = *Last;
*Last = UNICODE_NULL;
FindHandle = FindFirstFileW(Original, &FindFileData);
*Last = LastChar;
/* This portion wasn't found, so fail */
if (FindHandle == INVALID_HANDLE_VALUE)
{
ReturnLength = 0;
break;
}
/* Close the find handle */
FindClose(FindHandle);
/* Now check the length of the short name */
Length = wcslen(FindFileData.cAlternateFileName);
if (Length)
{
/* This is our new first marker */
First = FindFileData.cAlternateFileName;
}
else
{
/* Otherwise, the name is the delta between our current markers */
Length = Last - First;
}
/* Update the return length with the short name length, if any */
ReturnLength += Length;
/* Once again check for appropriate space and buffer */
if ((cchBuffer > ReturnLength) && (lpszShortPath))
{
/* And do the copy if there is */
RtlMoveMemory(Dst, First, Length * sizeof(WCHAR));
Dst += Length;
}
/* Now update the source pointer */
Src = Last;
if (*Src == UNICODE_NULL) break;
/* Are there more names in there? */
Found = FindLFNorSFN_U(Src, &First, &Last, TRUE);
if (!Found) break;
}
/* The loop is done, is there anything left? */
if (ReturnLength)
{
/* Get the length of the straggling path */
Length = wcslen(Src);
ReturnLength += Length;
/* Once again check for appropriate space and buffer */
if ((cchBuffer > ReturnLength) && (lpszShortPath))
{
/* And do the copy if there is -- accounting for NULL here */
RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR) + sizeof(UNICODE_NULL));
/* What about our buffer? */
if (Buffer)
{
/* Copy it into the caller's long path */
RtlMoveMemory(lpszShortPath,
Buffer,
ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
}
}
else
{
/* Buffer is too small, let the caller know, making space for NULL */
ReturnLength++;
}
}
/* We're all done */
goto Quickie;
ErrorQuickie:
/* This is the goto for memory failures */
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
Quickie:
/* General function end: free memory, restore error mode, return length */
if (Original) RtlFreeHeap(RtlGetProcessHeap(), 0, Original);
if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
SetErrorMode(ErrorMode);
return ReturnLength;
} }
/* EOF */ /* EOF */