Author: ion
Date: Sun Nov 6 21:00:10 2011
New Revision: 54321
URL: http://svn.reactos.org/svn/reactos?rev=54321&view=rev
Log:
[KERNEL32]: Fix infinite loop in FindLFNorSFN_U.
[KERNEL32]: Reimplement GetFullPathNameA to implement similar functionality and checks as the other reimplemented A functions, especially w.r.t ANSI/OEM and DBCS strings.
Modified:
trunk/reactos/dll/win32/kernel32/client/path.c
Modified: trunk/reactos/dll/win32/kernel32/client/path.c
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/kernel32/client/…
==============================================================================
--- trunk/reactos/dll/win32/kernel32/client/path.c [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/kernel32/client/path.c [iso-8859-1] Sun Nov 6 21:00:10 2011
@@ -187,11 +187,10 @@
* Check if it is valid
* Note that !IsShortName != IsLongName, these two functions simply help
* us determine if a conversion is necessary or not.
+ * "Found" really means: "Is a conversion necessary?", hence the "!"
*/
- Found = UseShort ? IsShortName_U(Path, Length) : IsLongName_U(Path, Length);
-
- /* "Found" really means: "Is a conversion necessary?", hence the ! */
- if (!Found)
+ Found = UseShort ? !IsShortName_U(Path, Length) : !IsLongName_U(Path, Length);
+ if (Found)
{
/* It is! did the caller request to know the markers? */
if ((First) && (Last))
@@ -211,7 +210,7 @@
}
/* Return if anything was found and valid */
- return !Found;
+ return Found;
}
PWCHAR
@@ -454,52 +453,113 @@
*/
DWORD
WINAPI
-GetFullPathNameA (
- LPCSTR lpFileName,
- DWORD nBufferLength,
- LPSTR lpBuffer,
- LPSTR *lpFilePart
- )
-{
- WCHAR BufferW[MAX_PATH];
- PWCHAR FileNameW;
- DWORD ret;
- LPWSTR FilePartW = NULL;
-
- DPRINT("GetFullPathNameA(lpFileName %s, nBufferLength %d, lpBuffer %p, "
- "lpFilePart %p)\n",lpFileName,nBufferLength,lpBuffer,lpFilePart);
-
- if (!(FileNameW = FilenameA2W(lpFileName, FALSE)))
- return 0;
-
- ret = GetFullPathNameW(FileNameW, MAX_PATH, BufferW, &FilePartW);
-
- if (!ret)
- return 0;
-
- if (ret > MAX_PATH)
- {
- SetLastError(ERROR_FILENAME_EXCED_RANGE);
- return 0;
- }
-
- ret = FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
-
- if (ret < nBufferLength && lpFilePart)
- {
- /* if the path closed with '\', FilePart is NULL */
- if (!FilePartW)
- *lpFilePart=NULL;
- else
- *lpFilePart = (FilePartW - BufferW) + lpBuffer;
- }
-
- DPRINT("GetFullPathNameA ret: lpBuffer %s lpFilePart %s\n",
- lpBuffer, (lpFilePart == NULL) ? "NULL" : *lpFilePart);
-
- return ret;
-}
-
+GetFullPathNameA(IN LPCSTR lpFileName,
+ IN DWORD nBufferLength,
+ IN LPSTR lpBuffer,
+ IN LPSTR *lpFilePart)
+{
+ NTSTATUS Status;
+ PWCHAR Buffer;
+ ULONG PathSize, FilePartSize;
+ ANSI_STRING AnsiString;
+ UNICODE_STRING FileNameString, UniString;
+ PWCHAR LocalFilePart;
+ PWCHAR* FilePart;
+
+ /* If the caller wants filepart, use a local wide buffer since this is A */
+ FilePart = lpFilePart != NULL ? &LocalFilePart : NULL;
+
+ /* Initialize for Quickie */
+ FilePartSize = PathSize = 0;
+ FileNameString.Buffer = NULL;
+
+ /* First get our string in Unicode */
+ Status = Basep8BitStringToDynamicUnicodeString(&FileNameString, lpFileName);
+ if (!NT_SUCCESS(Status)) goto Quickie;
+
+ /* Allocate a buffer to hold teh path name */
+ Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
+ 0,
+ MAX_PATH * sizeof(WCHAR) + sizeof(UNICODE_NULL));
+ if (!Buffer)
+ {
+ BaseSetLastNTError(STATUS_INSUFFICIENT_RESOURCES);
+ goto Quickie;
+ }
+
+ /* Call into RTL to get the full Unicode path name */
+ PathSize = RtlGetFullPathName_U(FileNameString.Buffer,
+ MAX_PATH * sizeof(WCHAR),
+ Buffer,
+ FilePart);
+ if (PathSize <= (MAX_PATH * sizeof(WCHAR)))
+ {
+ /* The buffer will fit, get the real ANSI string size now */
+ Status = RtlUnicodeToMultiByteSize(&PathSize, Buffer, PathSize);
+ if (NT_SUCCESS(Status))
+ {
+ /* Now check if the user wanted file part size as well */
+ if ((PathSize) && (lpFilePart) && (LocalFilePart))
+ {
+ /* Yep, so in this case get the length of the file part too */
+ Status = RtlUnicodeToMultiByteSize(&FilePartSize,
+ Buffer,
+ (LocalFilePart - Buffer) *
+ sizeof(WCHAR));
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed to do that, so fail the whole call */
+ BaseSetLastNTError(Status);
+ PathSize = 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Reset the path size since the buffer is not large enough */
+ PathSize = 0;
+ }
+
+ /* Either no path, or local buffer was too small, enter failure code */
+ if (!PathSize) goto Quickie;
+
+ /* If the *caller's* buffer was too small, fail, but add in space for NULL */
+ if (PathSize < nBufferLength)
+ {
+ PathSize++;
+ goto Quickie;
+ }
+
+ /* So far so good, initialize a unicode string to convert back to ANSI/OEM */
+ RtlInitUnicodeString(&UniString, Buffer);
+ Status = BasepUnicodeStringTo8BitString(&AnsiString, &UniString, TRUE);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Final conversion failed, fail the call */
+ BaseSetLastNTError(Status);
+ PathSize = 0;
+ }
+ else
+ {
+ /* Conversion worked, now copy the ANSI/OEM buffer into the buffer */
+ RtlCopyMemory(lpBuffer, AnsiString.Buffer, PathSize + 1);
+ RtlFreeAnsiString(&AnsiString);
+
+ /* And finally, did the caller request file part information? */
+ if (lpFilePart)
+ {
+ /* Use the size we computed earlier and add it to the buffer */
+ *lpFilePart = LocalFilePart ? &lpBuffer[FilePartSize] : 0;
+ }
+ }
+
+Quickie:
+ /* Cleanup and return the path size */
+ if (FileNameString.Buffer) RtlFreeUnicodeString(&FileNameString);
+ if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
+ return PathSize;
+}
/*
* @implemented
@@ -511,6 +571,7 @@
IN LPWSTR lpBuffer,
OUT LPWSTR *lpFilePart)
{
+ /* Call Rtl to do the work */
return RtlGetFullPathName_U((LPWSTR)lpFileName,
nBufferLength * sizeof(WCHAR),
lpBuffer,
Author: ion
Date: Sun Nov 6 19:42:59 2011
New Revision: 54320
URL: http://svn.reactos.org/svn/reactos?rev=54320&view=rev
Log:
[KERNEL32]: Fix some bugs in previous two commits (thanks Thomas).
Modified:
trunk/reactos/dll/win32/kernel32/client/path.c
Modified: trunk/reactos/dll/win32/kernel32/client/path.c
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/kernel32/client/…
==============================================================================
--- trunk/reactos/dll/win32/kernel32/client/path.c [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/kernel32/client/path.c [iso-8859-1] Sun Nov 6 19:42:59 2011
@@ -57,7 +57,7 @@
/* Sure, any emtpy name is a short name */
if (!Length) return TRUE;
- /* This could be . or .. or somethign else */
+ /* This could be . or .. or something else */
if (*Name == L'.')
{
/* Which one is it */
@@ -101,7 +101,7 @@
}
/* Check for illegal characters */
- if ((c > 0x7F) || (IllegalMask[c / 32] && (1 << (c % 32))))
+ if ((c > 0x7F) || (IllegalMask[c / 32] & (1 << (c % 32))))
{
return FALSE;
}
@@ -171,14 +171,14 @@
while (TRUE)
{
/* Loop within the path skipping slashes */
- while ((*Path) && ((*Path == L'\\') || (*Path == L'/'))) Path++;
+ while ((*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 */
+ /* Now skip past the file name until we get to the first slash */
p = Path + 1;
- while ((*p) && ((*p == L'\\') || (*p == L'/'))) p++;
+ while ((*p) && ((*p != L'\\') && (*p != L'/'))) p++;
/* Whatever is in between those two is now the file name length */
Length = p - Path;
@@ -804,7 +804,7 @@
ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
/* Do a simple check to see if the path exists */
- if (GetFileAttributesW(lpszShortPath) == 0xFFFFFFF)
+ if (GetFileAttributesW(lpszShortPath) == INVALID_FILE_ATTRIBUTES)
{
/* It doesn't, so fail */
ReturnLength = 0;
@@ -1111,7 +1111,7 @@
}
else
{
- PathLength = GetLongPathNameW(LongPathUni.Buffer, ShortPath, PathLength);
+ PathLength = GetShortPathNameW(LongPathUni.Buffer, ShortPath, PathLength);
}
}
@@ -1183,7 +1183,7 @@
ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
/* Do a simple check to see if the path exists */
- if (GetFileAttributesW(lpszShortPath) == 0xFFFFFFF)
+ if (GetFileAttributesW(lpszShortPath) == INVALID_FILE_ATTRIBUTES)
{
/* Windows checks for an application compatibility flag to allow this */
if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags.LowPart & 1))
Author: ion
Date: Sun Nov 6 17:17:16 2011
New Revision: 54318
URL: http://svn.reactos.org/svn/reactos?rev=54318&view=rev
Log:
[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.
Modified:
trunk/reactos/dll/win32/kernel32/client/path.c
Modified: trunk/reactos/dll/win32/kernel32/client/path.c
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/kernel32/client/…
==============================================================================
--- trunk/reactos/dll/win32/kernel32/client/path.c [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/kernel32/client/path.c [iso-8859-1] Sun Nov 6 17:17:16 2011
@@ -18,7 +18,241 @@
UNICODE_STRING BaseDllDirectory;
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 ******************************************************************/
+
+/*
+ * 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
WINAPI
@@ -536,108 +770,221 @@
return ret;
}
-/***********************************************************************
+/*
* @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 )
-{
-#define MAX_PATHNAME_LEN 1024
-
- WCHAR tmplongpath[MAX_PATHNAME_LEN];
- LPCWSTR p;
- DWORD sp = 0, lp = 0;
- DWORD tmplen;
- BOOL unixabsolute;
- WIN32_FIND_DATAW wfd;
- HANDLE goit;
-
- if (!shortpath)
- {
+ */
+DWORD
+WINAPI
+GetLongPathNameW(IN LPCWSTR lpszShortPath,
+ IN LPWSTR lpszLongPath,
+ IN DWORD cchBuffer)
+{
+ PWCHAR Path, Original, First, Last, Buffer, Src, Dst;
+ ULONG Length;
+ WCHAR LastChar;
+ HANDLE FindHandle;
+ DWORD ReturnLength;
+ ULONG ErrorMode;
+ BOOLEAN Found;
+ WIN32_FIND_DATAW FindFileData;
+
+ /* Initialize so Quickie knows there's nothing to do */
+ Buffer = Original = NULL;
+ ReturnLength = 0;
+
+ /* First check if the input path was obviously NULL */
+ if (!lpszShortPath)
+ {
+ /* Fail the request */
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
- if (!shortpath[0])
- {
- SetLastError(ERROR_PATH_NOT_FOUND);
- return 0;
- }
-
- DPRINT("GetLongPathNameW(%s,%p,%ld)\n", shortpath, longpath, longlen);
-
- if (shortpath[0] == '\\' && shortpath[1] == '\\')
- {
- 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])
- {
- /* check for path delimiters and reproduce them */
- if (shortpath[sp] == '\\' || shortpath[sp] == '/')
- {
- if (!lp || tmplongpath[lp-1] != '\\')
+
+ /* 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)
+ {
+ /* It doesn't, so fail */
+ ReturnLength = 0;
+ goto Quickie;
+ }
+
+ /* Now get a pointer to the actual path, skipping indicators */
+ Path = SkipPathTypeIndicator_U((PWCHAR)lpszShortPath);
+
+ /* Try to find a file name in there */
+ if (Path) Found = FindLFNorSFN_U(Path, &First, &Last, FALSE);
+
+ /* 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(lpszShortPath);
+
+ /* Is there space for it? */
+ if ((cchBuffer > ReturnLength) && (lpszLongPath))
+ {
+ /* Make sure the pointers aren't already the same */
+ if (lpszLongPath != lpszShortPath)
{
- /* strip double "\\" */
- tmplongpath[lp++] = '\\';
+ /* They're not -- copy the short path into the long path */
+ RtlMoveMemory(lpszLongPath,
+ lpszShortPath,
+ ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
}
- tmplongpath[lp] = 0; /* terminate string */
- sp++;
- continue;
- }
-
- p = shortpath + sp;
- if (sp == 0 && p[0] == '.' && (p[1] == '/' || p[1] == '\\'))
- {
- tmplongpath[lp++] = *p++;
- tmplongpath[lp++] = *p++;
- }
- for (; *p && *p != '/' && *p != '\\'; p++);
- 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;
- if (tmplen <= longlen)
- {
- wcscpy(longpath, tmplongpath);
- DPRINT("returning %s\n", longpath);
- tmplen--; /* length without 0 */
- }
-
- return tmplen;
+ }
+ else
+ {
+ /* Otherwise, let caller know we need a bigger buffer, include NULL */
+ ReturnLength++;
+ }
+ goto Quickie;
+ }
+
+ /* We are still in the game -- compute the current size */
+ 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))))
+ {
+ Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR));
+ if (!Buffer) goto ErrorQuickie;
+
+ /* New destination */
+ Dst = Buffer;
+ }
+
+ /* 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 @@
LongPathAnsi.Buffer = NULL;
ShortPathUni.Buffer = NULL;
Result = 0;
-
+
if (!lpszShortPath)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
-
+
Status = Basep8BitStringToDynamicUnicodeString(&ShortPathUni, lpszShortPath);
if (!NT_SUCCESS(Status)) goto Quickie;
@@ -686,7 +1033,7 @@
PathLength = GetLongPathNameW(ShortPathUni.Buffer, LongPath, PathLength);
}
}
-
+
if (!PathLength) goto Quickie;
ShortPathUni.MaximumLength = PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
@@ -741,13 +1088,13 @@
ShortPathAnsi.Buffer = NULL;
LongPathUni.Buffer = NULL;
Result = 0;
-
+
if (!lpszLongPath)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
-
+
Status = Basep8BitStringToDynamicUnicodeString(&LongPathUni, lpszLongPath);
if (!NT_SUCCESS(Status)) goto Quickie;
@@ -767,7 +1114,7 @@
PathLength = GetLongPathNameW(LongPathUni.Buffer, ShortPath, PathLength);
}
}
-
+
if (!PathLength) goto Quickie;
LongPathUni.MaximumLength = PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
@@ -802,109 +1149,225 @@
return Result;
}
-
-/*
- * NOTE: Copied from Wine.
+/*
* @implemented
*/
DWORD
WINAPI
-GetShortPathNameW (
- LPCWSTR longpath,
- LPWSTR shortpath,
- DWORD shortlen
- )
-{
- WCHAR tmpshortpath[MAX_PATH];
- LPCWSTR p;
- DWORD sp = 0, lp = 0;
- DWORD tmplen;
- WIN32_FIND_DATAW wfd;
- HANDLE goit;
- UNICODE_STRING ustr;
- WCHAR ustr_buf[8+1+3+1];
-
- DPRINT("GetShortPathNameW: %S\n",longpath);
-
- if (!longpath)
- {
+GetShortPathNameW(IN LPCWSTR lpszLongPath,
+ IN LPWSTR lpszShortPath,
+ IN DWORD cchBuffer)
+{
+ PWCHAR Path, Original, First, Last, Buffer, Src, Dst;
+ ULONG Length;
+ WCHAR LastChar;
+ HANDLE FindHandle;
+ DWORD ReturnLength;
+ ULONG ErrorMode;
+ BOOLEAN Found;
+ WIN32_FIND_DATAW FindFileData;
+
+ /* Initialize so Quickie knows there's nothing to do */
+ Buffer = Original = NULL;
+ ReturnLength = 0;
+
+ /* First check if the input path was obviously NULL */
+ if (!lpszLongPath)
+ {
+ /* Fail the request */
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
- if (!longpath[0])
- {
- SetLastError(ERROR_BAD_PATHNAME);
- return 0;
- }
-
- /* check for drive letter */
- if (longpath[0] != '/' && longpath[1] == ':' )
- {
- tmpshortpath[0] = longpath[0];
- tmpshortpath[1] = ':';
- sp = lp = 2;
- }
-
- ustr.Buffer = ustr_buf;
- ustr.Length = 0;
- ustr.MaximumLength = sizeof(ustr_buf);
-
- while (longpath[lp])
- {
- /* check for path delimiters and reproduce them */
- if (longpath[lp] == '\\' || longpath[lp] == '/')
- {
- if (!sp || tmpshortpath[sp-1] != '\\')
+
+ /* 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)
+ {
+ /* Windows checks for an application compatibility flag to allow this */
+ if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags.LowPart & 1))
+ {
+ /* It doesn't, so fail */
+ ReturnLength = 0;
+ goto Quickie;
+ }
+ }
+
+ /* Now get a pointer to the actual path, skipping indicators */
+ Path = SkipPathTypeIndicator_U((PWCHAR)lpszShortPath);
+
+ /* Try to find a file name in there */
+ 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))
+ {
+ /* Make sure the pointers aren't already the same */
+ if (lpszLongPath != lpszShortPath)
{
- /* strip double "\\" */
- tmpshortpath[sp] = '\\';
- sp++;
+ /* They're not -- copy the short path into the long path */
+ RtlMoveMemory(lpszShortPath,
+ lpszLongPath,
+ ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
}
- tmpshortpath[sp] = 0; /* terminate string */
- lp++;
- continue;
- }
-
- for (p = longpath + lp; *p && *p != '/' && *p != '\\'; p++);
- tmplen = p - (longpath + lp);
- lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
- /* Check, if the current element is a valid dos name */
- if (tmplen <= 8+1+3)
- {
- BOOLEAN spaces;
- memcpy(ustr_buf, longpath + lp, tmplen * sizeof(WCHAR));
- ustr_buf[tmplen] = '\0';
- ustr.Length = (USHORT)tmplen * sizeof(WCHAR);
- if (RtlIsNameLegalDOS8Dot3(&ustr, NULL, &spaces) && !spaces)
+ }
+ else
+ {
+ /* Otherwise, let caller know we need a bigger buffer, include NULL */
+ ReturnLength++;
+ }
+ goto Quickie;
+ }
+
+ /* We are still in the game -- compute the current size */
+ 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))))
+ {
+ Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR));
+ if (!Buffer) goto ErrorQuickie;
+
+ /* New destination */
+ Dst = Buffer;
+ }
+
+ /* 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) && (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)
{
- sp += tmplen;
- lp += tmplen;
- continue;
+ /* Copy it into the caller's long path */
+ RtlMoveMemory(lpszShortPath,
+ Buffer,
+ ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
}
}
-
- /* Check if the file exists and use the existing short file name */
- goit = FindFirstFileW(tmpshortpath, &wfd);
- if (goit == INVALID_HANDLE_VALUE) goto notfound;
- FindClose(goit);
- lstrcpyW(tmpshortpath + sp, wfd.cAlternateFileName);
- sp += lstrlenW(tmpshortpath + sp);
- lp += tmplen;
- }
- tmpshortpath[sp] = 0;
-
- tmplen = lstrlenW(tmpshortpath) + 1;
- if (tmplen <= shortlen)
- {
- lstrcpyW(shortpath, tmpshortpath);
- tmplen--; /* length without 0 */
- }
-
- return tmplen;
-
- notfound:
- SetLastError ( ERROR_FILE_NOT_FOUND );
- return 0;
+ 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 */