From 6c8ee62340043117603548f68ba3b07a65a7db33 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 20 Jan 2004 01:40:23 +0000 Subject: [PATCH] - RtlGetFullPathName_U: rewritten so that the source & destination buffer can be the same - GetFullPathName[AW]: now call RtlGetFullPathName_U - GetShortPathNameW: fixed regression introduced in last patch --- dlls/kernel/path.c | 85 ++++++++++++++-- dlls/ntdll/path.c | 165 +++++++++++++----------------- files/dos_fs.c | 246 --------------------------------------------- 3 files changed, 147 insertions(+), 349 deletions(-) diff --git a/dlls/kernel/path.c b/dlls/kernel/path.c index 08f1ed4ff0d..7812b69b767 100644 --- a/dlls/kernel/path.c +++ b/dlls/kernel/path.c @@ -42,6 +42,76 @@ WINE_DEFAULT_DEBUG_CHANNEL(file); #define MAX_PATHNAME_LEN 1024 +/*********************************************************************** + * GetFullPathNameW (KERNEL32.@) + * NOTES + * if the path closed with '\', *lastpart is 0 + */ +DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer, + LPWSTR *lastpart ) +{ + return RtlGetFullPathName_U(name, len * sizeof(WCHAR), buffer, lastpart) / sizeof(WCHAR); +} + +/*********************************************************************** + * GetFullPathNameA (KERNEL32.@) + * NOTES + * if the path closed with '\', *lastpart is 0 + */ +DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer, + LPSTR *lastpart ) +{ + UNICODE_STRING nameW; + WCHAR bufferW[MAX_PATH]; + DWORD ret, retW; + + if (!name) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name)) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + + retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL); + + if (!retW) + ret = 0; + else if (retW > MAX_PATH) + { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + ret = 0; + } + else + { + ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL); + if (ret && ret <= len) + { + WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL); + ret--; /* length without 0 */ + + if (lastpart) + { + LPSTR p = buffer + strlen(buffer) - 1; + + if (*p != '\\') + { + while ((p > buffer + 2) && (*p != '\\')) p--; + *lastpart = p + 1; + } + else *lastpart = NULL; + } + } + } + + RtlFreeUnicodeString(&nameW); + return ret; +} + /*********************************************************************** * GetLongPathNameW (KERNEL32.@) @@ -266,20 +336,21 @@ DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortl 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+1) { memcpy(ustr_buf, longpath + lp, tmplen * sizeof(WCHAR)); ustr_buf[tmplen] = '\0'; ustr.Length = tmplen * sizeof(WCHAR); - /* Check, if the current element is a valid dos name */ - if (!RtlIsNameLegalDOS8Dot3(&ustr, NULL, NULL)) - goto notfound; - sp += tmplen; - lp += tmplen; - continue; + if (RtlIsNameLegalDOS8Dot3(&ustr, NULL, NULL)) + { + sp += tmplen; + lp += tmplen; + continue; + } } - /* Check if the file exists and use the existing file name */ + /* 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); diff --git a/dlls/ntdll/path.c b/dlls/ntdll/path.c index cfe236196e5..b4e7bd7d95a 100644 --- a/dlls/ntdll/path.c +++ b/dlls/ntdll/path.c @@ -320,54 +320,49 @@ ULONG WINAPI RtlDosSearchPath_U(LPCWSTR paths, LPCWSTR search, LPCWSTR ext, * get_full_path_helper * * Helper for RtlGetFullPathName_U + * Note: name and buffer are allowed to point to the same memory spot */ static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size) { - ULONG reqsize, mark = 0; - DOS_PATHNAME_TYPE type; - LPWSTR ptr; - UNICODE_STRING* cd; - - reqsize = sizeof(WCHAR); /* '\0' at the end */ - + ULONG reqsize = 0, mark = 0, dep = 0, deplen; + DOS_PATHNAME_TYPE type; + LPWSTR ptr, ins_str = NULL; + const UNICODE_STRING* cd; + WCHAR tmp[4]; + RtlAcquirePebLock(); + cd = &ntdll_get_process_pmts()->CurrentDirectoryName; switch (type = RtlDetermineDosPathNameType_U(name)) { case UNC_PATH: /* \\foo */ case DEVICE_PATH: /* \\.\foo */ - if (reqsize <= size) buffer[0] = '\0'; break; case ABSOLUTE_DRIVE_PATH: /* c:\foo */ - reqsize += sizeof(WCHAR); - if (reqsize <= size) - { - buffer[0] = toupperW(name[0]); - buffer[1] = '\0'; - } - name++; + reqsize = sizeof(WCHAR); + tmp[0] = toupperW(name[0]); + ins_str = tmp; + dep = 1; break; case RELATIVE_DRIVE_PATH: /* c:foo */ + dep = 2; if (toupperW(name[0]) != toupperW(cd->Buffer[0]) || cd->Buffer[1] != ':') { - WCHAR drive[4]; UNICODE_STRING var, val; - drive[0] = '='; - drive[1] = name[0]; - drive[2] = ':'; - drive[3] = '\0'; - var.Length = 6; - var.MaximumLength = 8; - var.Buffer = drive; + tmp[0] = '='; + tmp[1] = name[0]; + tmp[2] = ':'; + tmp[3] = '\0'; + var.Length = 3 * sizeof(WCHAR); + var.MaximumLength = 4 * sizeof(WCHAR); + var.Buffer = tmp; val.Length = 0; val.MaximumLength = size; - val.Buffer = buffer; - - name += 2; + val.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, size); switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val)) { @@ -379,24 +374,16 @@ static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size) */ /* fall thru */ case STATUS_BUFFER_TOO_SMALL: - reqsize += val.Length; - /* append trailing \\ */ - reqsize += sizeof(WCHAR); - if (reqsize <= size) - { - buffer[reqsize / sizeof(WCHAR) - 2] = '\\'; - buffer[reqsize / sizeof(WCHAR) - 1] = '\0'; - } + reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */ + val.Buffer[val.Length / sizeof(WCHAR)] = '\\'; + ins_str = val.Buffer; break; case STATUS_VARIABLE_NOT_FOUND: - reqsize += 3 * sizeof(WCHAR); - if (reqsize <= size) - { - buffer[0] = drive[1]; - buffer[1] = ':'; - buffer[2] = '\\'; - buffer[3] = '\0'; - } + reqsize = 3 * sizeof(WCHAR); + tmp[0] = name[0]; + tmp[1] = ':'; + tmp[2] = '\\'; + ins_str = tmp; break; default: ERR("Unsupported status code\n"); @@ -404,16 +391,11 @@ static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size) } break; } - name += 2; /* fall through */ case RELATIVE_PATH: /* foo */ - reqsize += cd->Length; - if (reqsize <= size) - { - memcpy(buffer, cd->Buffer, cd->Length); - buffer[cd->Length / sizeof(WCHAR)] = 0; - } + reqsize = cd->Length; + ins_str = cd->Buffer; if (cd->Buffer[1] != ':') { ptr = strchrW(cd->Buffer + 2, '\\'); @@ -426,69 +408,62 @@ static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size) case ABSOLUTE_PATH: /* \xxx */ if (cd->Buffer[1] == ':') { - reqsize += 2 * sizeof(WCHAR); - if (reqsize <= size) - { - buffer[0] = cd->Buffer[0]; - buffer[1] = ':'; - buffer[2] = '\0'; - } + reqsize = 2 * sizeof(WCHAR); + tmp[0] = cd->Buffer[0]; + tmp[1] = ':'; + ins_str = tmp; } else { - unsigned len; - ptr = strchrW(cd->Buffer + 2, '\\'); if (ptr) ptr = strchrW(ptr + 1, '\\'); if (!ptr) ptr = cd->Buffer + strlenW(cd->Buffer); - len = (ptr - cd->Buffer) * sizeof(WCHAR); - reqsize += len; - mark = len / sizeof(WCHAR); - if (reqsize <= size) - { - memcpy(buffer, cd->Buffer, len); - buffer[len / sizeof(WCHAR)] = '\0'; - } - else - buffer[0] = '\0'; + reqsize = (ptr - cd->Buffer) * sizeof(WCHAR); + mark = reqsize / sizeof(WCHAR); + ins_str = cd->Buffer; } break; case UNC_DOT_PATH: /* \\. */ - reqsize += 4 * sizeof(WCHAR); - name += 3; - if (reqsize <= size) - { - buffer[0] = '\\'; - buffer[1] = '\\'; - buffer[2] = '.'; - buffer[3] = '\\'; - buffer[4] = '\0'; - } + reqsize = 4 * sizeof(WCHAR); + dep = 3; + tmp[0] = '\\'; + tmp[1] = '\\'; + tmp[2] = '.'; + tmp[3] = '\\'; + ins_str = tmp; break; case INVALID_PATH: - reqsize = 0; goto done; } - reqsize += strlenW(name) * sizeof(WCHAR); - if (reqsize > size) goto done; + /* enough space ? */ + deplen = strlenW(name + dep) * sizeof(WCHAR); + if (reqsize + deplen + sizeof(WCHAR) > size) + { + /* not enough space, return need size (including terminating '\0') */ + reqsize += deplen + sizeof(WCHAR); + goto done; + } - strcatW(buffer, name); + memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR)); + if (reqsize) memcpy(buffer, ins_str, reqsize); + reqsize += deplen; + + if (ins_str && ins_str != tmp && ins_str != cd->Buffer) + RtlFreeHeap(GetProcessHeap(), 0, ins_str); /* convert every / into a \ */ - for (ptr = buffer; *ptr; ptr++) - if (*ptr == '/') *ptr = '\\'; - - reqsize -= sizeof(WCHAR); /* don't count trailing \0 */ + for (ptr = buffer; *ptr; ptr++) if (*ptr == '/') *ptr = '\\'; /* mark is non NULL for UNC names, so start path collapsing after server & share name * otherwise, it's a fully qualified DOS name, so start after the drive designation */ for (ptr = buffer + (mark ? mark : 2); ptr < buffer + reqsize / sizeof(WCHAR); ) { - WCHAR* p = strchrW(ptr, '\\'); + LPWSTR prev, p = strchrW(ptr, '\\'); + if (!p) break; p++; @@ -500,15 +475,13 @@ static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size) switch (p[2]) { case '\\': - { - WCHAR* prev = p - 2; - while (prev >= buffer + mark && *prev != '\\') prev--; - /* either collapse \foo\.. into \ or \.. into \ */ - if (prev < buffer + mark) prev = p - 1; - reqsize -= (p + 2 - prev) * sizeof(WCHAR); - memmove(prev, p + 2, reqsize + sizeof(WCHAR) - (prev - buffer) * sizeof(WCHAR)); - p = prev; - } + prev = p - 2; + while (prev >= buffer + mark && *prev != '\\') prev--; + /* either collapse \foo\.. into \ or \.. into \ */ + if (prev < buffer + mark) prev = p - 1; + reqsize -= (p + 2 - prev) * sizeof(WCHAR); + memmove(prev, p + 2, reqsize + sizeof(WCHAR) - (prev - buffer) * sizeof(WCHAR)); + p = prev; break; case '\0': reqsize -= 2 * sizeof(WCHAR); diff --git a/files/dos_fs.c b/files/dos_fs.c index a86d57bc163..6a23aeb99d0 100644 --- a/files/dos_fs.c +++ b/files/dos_fs.c @@ -1172,252 +1172,6 @@ BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full ) } -/*********************************************************************** - * DOSFS_DoGetFullPathName - * - * Implementation of GetFullPathNameA/W. - * - * bon@elektron 000331: - * A test for GetFullPathName with many pathological cases - * now gives identical output for Wine and OSR2 - */ -static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result ) -{ - DWORD ret; - DOS_FULL_NAME full_name; - LPWSTR p, q; - char *p_l; - const char * root; - WCHAR drivecur[] = {'C',':','.',0}; - WCHAR driveletter=0; - int namelen,drive=0; - static const WCHAR bkslashW[] = {'\\',0}; - static const WCHAR dotW[] = {'.',0}; - static const WCHAR updir_slashW[] = {'\\','.','.','\\',0}; - static const WCHAR curdirW[] = {'\\','.','\\',0}; - static const WCHAR updirW[] = {'\\','.','.',0}; - - if (!name[0]) - { - SetLastError(ERROR_BAD_PATHNAME); - return 0; - } - - TRACE("passed %s\n", debugstr_w(name)); - - if (name[1]==':') - /*drive letter given */ - { - driveletter = name[0]; - } - if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/'))) - /*absolute path given */ - { - strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN); - full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */ - drive = toupperW(name[0]) - 'A'; - } - else - { - if (driveletter) - drivecur[0]=driveletter; - else if ((name[0]=='\\') || (name[0]=='/')) - strcpyW(drivecur, bkslashW); - else - strcpyW(drivecur, dotW); - - if (!DOSFS_GetFullName( drivecur, FALSE, &full_name )) - { - FIXME("internal: error getting drive/path\n"); - return 0; - } - /* find path that drive letter substitutes*/ - drive = toupperW(full_name.short_name[0]) - 'A'; - root= DRIVE_GetRoot(drive); - if (!root) - { - FIXME("internal: error getting DOS Drive Root\n"); - return 0; - } - if (!strcmp(root,"/")) - { - /* we have just the last / and we need it. */ - p_l = full_name.long_name; - } - else - { - p_l = full_name.long_name + strlen(root); - } - /* append long name (= unix name) to drive */ - MultiByteToWideChar(CP_UNIXCP, 0, p_l, -1, full_name.short_name + 2, MAX_PATHNAME_LEN - 3); - /* append name to treat */ - namelen= strlenW(full_name.short_name); - p = (LPWSTR)name; - if (driveletter) - p += 2; /* skip drive name when appending */ - if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN) - { - FIXME("internal error: buffer too small\n"); - return 0; - } - full_name.short_name[namelen++] ='\\'; - full_name.short_name[namelen] = 0; - strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen); - full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */ - } - /* reverse all slashes */ - for (p=full_name.short_name; - p < full_name.short_name + strlenW(full_name.short_name); - p++) - { - if ( *p == '/' ) - *p = '\\'; - } - /* Use memmove, as areas overlap */ - /* Delete .. */ - while ((p = strstrW(full_name.short_name, updir_slashW))) - { - if (p > full_name.short_name+2) - { - *p = 0; - q = strrchrW(full_name.short_name, '\\'); - memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR)); - } - else - { - memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR)); - } - } - if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.')) - { - /* This case istn't treated yet : c:..\test */ - memmove(full_name.short_name+2,full_name.short_name+4, - (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR)); - } - /* Delete . */ - while ((p = strstrW(full_name.short_name, curdirW))) - { - *(p+1) = 0; - memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR)); - } - if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING)) - for (p = full_name.short_name; *p; p++) *p = toupperW(*p); - namelen = strlenW(full_name.short_name); - if (!strcmpW(full_name.short_name+namelen-3, updirW)) - { - /* one more strange case: "c:\test\test1\.." - return "c:\test" */ - *(full_name.short_name+namelen-3)=0; - q = strrchrW(full_name.short_name, '\\'); - *q =0; - } - if (full_name.short_name[namelen-1]=='.') - full_name.short_name[(namelen--)-1] =0; - TRACE("got %s\n", debugstr_w(full_name.short_name)); - - /* If the lpBuffer buffer is too small, the return value is the - size of the buffer, in characters, required to hold the path - plus the terminating \0 (tested against win95osr2, bon 001118) - . */ - ret = strlenW(full_name.short_name); - if (ret >= len ) - { - /* don't touch anything when the buffer is not large enough */ - SetLastError( ERROR_INSUFFICIENT_BUFFER ); - return ret+1; - } - if (result) - { - strncpyW( result, full_name.short_name, len ); - result[len - 1] = 0; /* ensure 0 termination */ - } - - TRACE("returning %s\n", debugstr_w(full_name.short_name) ); - return ret; -} - - -/*********************************************************************** - * GetFullPathNameA (KERNEL32.@) - * NOTES - * if the path closed with '\', *lastpart is 0 - */ -DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer, - LPSTR *lastpart ) -{ - UNICODE_STRING nameW; - WCHAR bufferW[MAX_PATH]; - DWORD ret, retW; - - if (!name) - { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name)) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - - retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL); - - if (!retW) - ret = 0; - else if (retW > MAX_PATH) - { - SetLastError(ERROR_FILENAME_EXCED_RANGE); - ret = 0; - } - else - { - ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL); - if (ret <= len) - { - WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL); - ret--; /* length without 0 */ - - if (lastpart) - { - LPSTR p = buffer + strlen(buffer) - 1; - - if (*p != '\\') - { - while ((p > buffer + 2) && (*p != '\\')) p--; - *lastpart = p + 1; - } - else *lastpart = NULL; - } - } - } - - RtlFreeUnicodeString(&nameW); - return ret; -} - - -/*********************************************************************** - * GetFullPathNameW (KERNEL32.@) - */ -DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer, - LPWSTR *lastpart ) -{ - DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer ); - if (ret && (ret<=len) && buffer && lastpart) - { - LPWSTR p = buffer + strlenW(buffer) - 1; - if (*p != (WCHAR)'\\') - { - while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--; - *lastpart = p + 1; - } - else *lastpart = NULL; - } - return ret; -} - - /*********************************************************************** * wine_get_unix_file_name (KERNEL32.@) Not a Windows API *