/* * setupapi query functions * * Copyright 2006 James Hawkins * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include <stdarg.h> #include "windef.h" #include "winbase.h" #include "winuser.h" #include "winreg.h" #include "setupapi.h" #include "advpub.h" #include "winnls.h" #include "wine/debug.h" #include "wine/unicode.h" #include "setupapi_private.h" WINE_DEFAULT_DEBUG_CHANNEL(setupapi); #ifdef __i386__ static const WCHAR source_disks_names_platform[] = {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s','.','x','8','6',0}; static const WCHAR source_disks_files_platform[] = {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s','.','x','8','6',0}; #elif defined(__x86_64) static const WCHAR source_disks_names_platform[] = {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s','.','a','m','d','6','4',0}; static const WCHAR source_disks_files_platform[] = {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s','.','a','m','d','6','4',0}; #else /* FIXME: other platforms */ static const WCHAR source_disks_names_platform[] = {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s',0}; static const WCHAR source_disks_files_platform[] = {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s',0}; #endif static const WCHAR source_disks_names[] = {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s',0}; static const WCHAR source_disks_files[] = {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s',0}; /* fills the PSP_INF_INFORMATION struct fill_info is TRUE * always returns the required size of the information */ static BOOL fill_inf_info(HINF inf, PSP_INF_INFORMATION buffer, DWORD size, DWORD *required) { LPCWSTR filename = PARSER_get_inf_filename(inf); DWORD total_size = FIELD_OFFSET(SP_INF_INFORMATION, VersionData) + (lstrlenW(filename) + 1) * sizeof(WCHAR); if (required) *required = total_size; /* FIXME: we need to parse the INF file to find the correct version info */ if (buffer) { if (size < total_size) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; } buffer->InfStyle = INF_STYLE_WIN4; buffer->InfCount = 1; /* put the filename in buffer->VersionData */ lstrcpyW((LPWSTR)&buffer->VersionData[0], filename); } return TRUE; } static HINF search_for_inf(LPCVOID InfSpec, DWORD SearchControl) { HINF hInf = INVALID_HANDLE_VALUE; WCHAR inf_path[MAX_PATH]; static const WCHAR infW[] = {'\\','i','n','f','\\',0}; static const WCHAR system32W[] = {'\\','s','y','s','t','e','m','3','2','\\',0}; if (SearchControl == INFINFO_REVERSE_DEFAULT_SEARCH) { GetWindowsDirectoryW(inf_path, MAX_PATH); lstrcatW(inf_path, system32W); lstrcatW(inf_path, InfSpec); hInf = SetupOpenInfFileW(inf_path, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL); if (hInf != INVALID_HANDLE_VALUE) return hInf; GetWindowsDirectoryW(inf_path, MAX_PATH); lstrcpyW(inf_path, infW); lstrcatW(inf_path, InfSpec); return SetupOpenInfFileW(inf_path, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL); } return INVALID_HANDLE_VALUE; } /*********************************************************************** * SetupGetInfInformationA (SETUPAPI.@) * */ BOOL WINAPI SetupGetInfInformationA(LPCVOID InfSpec, DWORD SearchControl, PSP_INF_INFORMATION ReturnBuffer, DWORD ReturnBufferSize, PDWORD RequiredSize) { LPWSTR inf = (LPWSTR)InfSpec; DWORD len; BOOL ret; if (InfSpec && SearchControl >= INFINFO_INF_NAME_IS_ABSOLUTE) { len = lstrlenA(InfSpec) + 1; inf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); MultiByteToWideChar(CP_ACP, 0, InfSpec, -1, inf, len); } ret = SetupGetInfInformationW(inf, SearchControl, ReturnBuffer, ReturnBufferSize, RequiredSize); if (SearchControl >= INFINFO_INF_NAME_IS_ABSOLUTE) HeapFree(GetProcessHeap(), 0, inf); return ret; } /*********************************************************************** * SetupGetInfInformationW (SETUPAPI.@) * * BUGS * Only handles the case when InfSpec is an INF handle. */ BOOL WINAPI SetupGetInfInformationW(LPCVOID InfSpec, DWORD SearchControl, PSP_INF_INFORMATION ReturnBuffer, DWORD ReturnBufferSize, PDWORD RequiredSize) { HINF inf; BOOL ret; DWORD infSize; TRACE("(%p, %d, %p, %d, %p)\n", InfSpec, SearchControl, ReturnBuffer, ReturnBufferSize, RequiredSize); if (!InfSpec) { if (SearchControl == INFINFO_INF_SPEC_IS_HINF) SetLastError(ERROR_INVALID_HANDLE); else SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } switch (SearchControl) { case INFINFO_INF_SPEC_IS_HINF: inf = (HINF)InfSpec; break; case INFINFO_INF_NAME_IS_ABSOLUTE: case INFINFO_DEFAULT_SEARCH: inf = SetupOpenInfFileW(InfSpec, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL); break; case INFINFO_REVERSE_DEFAULT_SEARCH: inf = search_for_inf(InfSpec, SearchControl); break; case INFINFO_INF_PATH_LIST_SEARCH: FIXME("Unhandled search control: %d\n", SearchControl); if (RequiredSize) *RequiredSize = 0; return FALSE; default: SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (inf == INVALID_HANDLE_VALUE) { SetLastError(ERROR_FILE_NOT_FOUND); return FALSE; } ret = fill_inf_info(inf, ReturnBuffer, ReturnBufferSize, &infSize); if (!ReturnBuffer && (ReturnBufferSize >= infSize)) { SetLastError(ERROR_INVALID_PARAMETER); ret = FALSE; } if (RequiredSize) *RequiredSize = infSize; if (SearchControl >= INFINFO_INF_NAME_IS_ABSOLUTE) SetupCloseInfFile(inf); return ret; } /*********************************************************************** * SetupQueryInfFileInformationA (SETUPAPI.@) */ BOOL WINAPI SetupQueryInfFileInformationA(PSP_INF_INFORMATION InfInformation, UINT InfIndex, PSTR ReturnBuffer, DWORD ReturnBufferSize, PDWORD RequiredSize) { LPWSTR filenameW; DWORD size; BOOL ret; ret = SetupQueryInfFileInformationW(InfInformation, InfIndex, NULL, 0, &size); if (!ret) return FALSE; filenameW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); ret = SetupQueryInfFileInformationW(InfInformation, InfIndex, filenameW, size, &size); if (!ret) { HeapFree(GetProcessHeap(), 0, filenameW); return FALSE; } if (RequiredSize) *RequiredSize = size; if (!ReturnBuffer) { HeapFree(GetProcessHeap(), 0, filenameW); if (ReturnBufferSize) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } return TRUE; } if (size > ReturnBufferSize) { HeapFree(GetProcessHeap(), 0, filenameW); SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; } WideCharToMultiByte(CP_ACP, 0, filenameW, -1, ReturnBuffer, size, NULL, NULL); HeapFree(GetProcessHeap(), 0, filenameW); return ret; } /*********************************************************************** * SetupQueryInfFileInformationW (SETUPAPI.@) */ BOOL WINAPI SetupQueryInfFileInformationW(PSP_INF_INFORMATION InfInformation, UINT InfIndex, PWSTR ReturnBuffer, DWORD ReturnBufferSize, PDWORD RequiredSize) { DWORD len; LPWSTR ptr; TRACE("(%p, %u, %p, %d, %p) Stub!\n", InfInformation, InfIndex, ReturnBuffer, ReturnBufferSize, RequiredSize); if (!InfInformation) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (InfIndex != 0) FIXME("Appended INF files are not handled\n"); ptr = (LPWSTR)&InfInformation->VersionData[0]; len = lstrlenW(ptr); if (RequiredSize) *RequiredSize = len + 1; if (!ReturnBuffer) return TRUE; if (ReturnBufferSize < len) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; } lstrcpyW(ReturnBuffer, ptr); return TRUE; } /*********************************************************************** * SetupGetSourceFileLocationA (SETUPAPI.@) */ BOOL WINAPI SetupGetSourceFileLocationA( HINF hinf, PINFCONTEXT context, PCSTR filename, PUINT source_id, PSTR buffer, DWORD buffer_size, PDWORD required_size ) { BOOL ret = FALSE; WCHAR *filenameW = NULL, *bufferW = NULL; DWORD required; INT size; TRACE("%p, %p, %s, %p, %p, 0x%08x, %p\n", hinf, context, debugstr_a(filename), source_id, buffer, buffer_size, required_size); if (filename && *filename && !(filenameW = strdupAtoW( filename ))) return FALSE; if (!SetupGetSourceFileLocationW( hinf, context, filenameW, source_id, NULL, 0, &required )) goto done; if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) ))) goto done; if (!SetupGetSourceFileLocationW( hinf, context, filenameW, source_id, bufferW, required, NULL )) goto done; size = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL ); if (required_size) *required_size = size; if (buffer) { if (buffer_size >= size) WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, buffer_size, NULL, NULL ); else { SetLastError( ERROR_INSUFFICIENT_BUFFER ); goto done; } } ret = TRUE; done: HeapFree( GetProcessHeap(), 0, filenameW ); HeapFree( GetProcessHeap(), 0, bufferW ); return ret; } static LPWSTR get_source_id( HINF hinf, PINFCONTEXT context, PCWSTR filename ) { DWORD size; LPWSTR source_id; if (!SetupFindFirstLineW( hinf, source_disks_files_platform, filename, context ) && !SetupFindFirstLineW( hinf, source_disks_files, filename, context )) return NULL; if (!SetupGetStringFieldW( context, 1, NULL, 0, &size )) return NULL; if (!(source_id = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL; if (!SetupGetStringFieldW( context, 1, source_id, size, NULL )) { HeapFree( GetProcessHeap(), 0, source_id ); return NULL; } if (!SetupFindFirstLineW( hinf, source_disks_names_platform, source_id, context ) && !SetupFindFirstLineW( hinf, source_disks_names, source_id, context )) { HeapFree( GetProcessHeap(), 0, source_id ); return NULL; } return source_id; } /*********************************************************************** * SetupGetSourceFileLocationW (SETUPAPI.@) */ BOOL WINAPI SetupGetSourceFileLocationW( HINF hinf, PINFCONTEXT context, PCWSTR filename, PUINT source_id, PWSTR buffer, DWORD buffer_size, PDWORD required_size ) { INFCONTEXT ctx; WCHAR *end, *source_id_str; TRACE("%p, %p, %s, %p, %p, 0x%08x, %p\n", hinf, context, debugstr_w(filename), source_id, buffer, buffer_size, required_size); if (!context) context = &ctx; if (!(source_id_str = get_source_id( hinf, context, filename ))) return FALSE; *source_id = strtolW( source_id_str, &end, 10 ); if (end == source_id_str || *end) { HeapFree( GetProcessHeap(), 0, source_id_str ); return FALSE; } HeapFree( GetProcessHeap(), 0, source_id_str ); if (SetupGetStringFieldW( context, 4, buffer, buffer_size, required_size )) return TRUE; if (required_size) *required_size = 1; if (buffer) { if (buffer_size >= 1) buffer[0] = 0; else { SetLastError( ERROR_INSUFFICIENT_BUFFER ); return FALSE; } } return TRUE; } /*********************************************************************** * SetupGetSourceInfoA (SETUPAPI.@) */ BOOL WINAPI SetupGetSourceInfoA( HINF hinf, UINT source_id, UINT info, PSTR buffer, DWORD buffer_size, LPDWORD required_size ) { BOOL ret = FALSE; WCHAR *bufferW = NULL; DWORD required; INT size; TRACE("%p, %d, %d, %p, %d, %p\n", hinf, source_id, info, buffer, buffer_size, required_size); if (!SetupGetSourceInfoW( hinf, source_id, info, NULL, 0, &required )) return FALSE; if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) ))) return FALSE; if (!SetupGetSourceInfoW( hinf, source_id, info, bufferW, required, NULL )) goto done; size = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL ); if (required_size) *required_size = size; if (buffer) { if (buffer_size >= size) WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, buffer_size, NULL, NULL ); else { SetLastError( ERROR_INSUFFICIENT_BUFFER ); goto done; } } ret = TRUE; done: HeapFree( GetProcessHeap(), 0, bufferW ); return ret; } /*********************************************************************** * SetupGetSourceInfoW (SETUPAPI.@) */ BOOL WINAPI SetupGetSourceInfoW( HINF hinf, UINT source_id, UINT info, PWSTR buffer, DWORD buffer_size, LPDWORD required_size ) { INFCONTEXT ctx; WCHAR source_id_str[11]; static const WCHAR fmt[] = {'%','d',0}; DWORD index; TRACE("%p, %d, %d, %p, %d, %p\n", hinf, source_id, info, buffer, buffer_size, required_size); sprintfW( source_id_str, fmt, source_id ); if (!SetupFindFirstLineW( hinf, source_disks_names_platform, source_id_str, &ctx ) && !SetupFindFirstLineW( hinf, source_disks_names, source_id_str, &ctx )) return FALSE; switch (info) { case SRCINFO_PATH: index = 4; break; case SRCINFO_TAGFILE: index = 2; break; case SRCINFO_DESCRIPTION: index = 1; break; default: WARN("unknown info level: %d\n", info); return FALSE; } if (SetupGetStringFieldW( &ctx, index, buffer, buffer_size, required_size )) return TRUE; if (required_size) *required_size = 1; if (buffer) { if (buffer_size >= 1) buffer[0] = 0; else { SetLastError( ERROR_INSUFFICIENT_BUFFER ); return FALSE; } } return TRUE; } /*********************************************************************** * SetupGetTargetPathA (SETUPAPI.@) */ BOOL WINAPI SetupGetTargetPathA( HINF hinf, PINFCONTEXT context, PCSTR section, PSTR buffer, DWORD buffer_size, PDWORD required_size ) { BOOL ret = FALSE; WCHAR *sectionW = NULL, *bufferW = NULL; DWORD required; INT size; TRACE("%p, %p, %s, %p, 0x%08x, %p\n", hinf, context, debugstr_a(section), buffer, buffer_size, required_size); if (section && !(sectionW = strdupAtoW( section ))) return FALSE; if (!SetupGetTargetPathW( hinf, context, sectionW, NULL, 0, &required )) goto done; if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) ))) goto done; if (!SetupGetTargetPathW( hinf, context, sectionW, bufferW, required, NULL )) goto done; size = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL ); if (required_size) *required_size = size; if (buffer) { if (buffer_size >= size) WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, buffer_size, NULL, NULL ); else { SetLastError( ERROR_INSUFFICIENT_BUFFER ); goto done; } } ret = TRUE; done: HeapFree( GetProcessHeap(), 0, sectionW ); HeapFree( GetProcessHeap(), 0, bufferW ); return ret; } /*********************************************************************** * SetupGetTargetPathW (SETUPAPI.@) */ BOOL WINAPI SetupGetTargetPathW( HINF hinf, PINFCONTEXT context, PCWSTR section, PWSTR buffer, DWORD buffer_size, PDWORD required_size ) { static const WCHAR destination_dirs[] = {'D','e','s','t','i','n','a','t','i','o','n','D','i','r','s',0}; static const WCHAR default_dest_dir[] = {'D','e','f','a','u','l','t','D','e','s','t','D','i','r',0}; INFCONTEXT ctx; WCHAR *dir, systemdir[MAX_PATH]; unsigned int size; BOOL ret = FALSE; TRACE("%p, %p, %s, %p, 0x%08x, %p\n", hinf, context, debugstr_w(section), buffer, buffer_size, required_size); if (context) ret = SetupFindFirstLineW( hinf, destination_dirs, NULL, context ); else if (section) { if (!(ret = SetupFindFirstLineW( hinf, destination_dirs, section, &ctx ))) ret = SetupFindFirstLineW( hinf, destination_dirs, default_dest_dir, &ctx ); } if (!ret || !(dir = PARSER_get_dest_dir( context ? context : &ctx ))) { GetSystemDirectoryW( systemdir, MAX_PATH ); dir = systemdir; } size = strlenW( dir ) + 1; if (required_size) *required_size = size; if (buffer) { if (buffer_size >= size) lstrcpyW( buffer, dir ); else { SetLastError( ERROR_INSUFFICIENT_BUFFER ); HeapFree( GetProcessHeap(), 0, dir ); return FALSE; } } if (dir != systemdir) HeapFree( GetProcessHeap(), 0, dir ); return TRUE; } /*********************************************************************** * SetupQueryInfOriginalFileInformationA (SETUPAPI.@) */ BOOL WINAPI SetupQueryInfOriginalFileInformationA( PSP_INF_INFORMATION InfInformation, UINT InfIndex, PSP_ALTPLATFORM_INFO AlternativePlatformInfo, PSP_ORIGINAL_FILE_INFO_A OriginalFileInfo) { BOOL ret; SP_ORIGINAL_FILE_INFO_W OriginalFileInfoW; TRACE("(%p, %d, %p, %p)\n", InfInformation, InfIndex, AlternativePlatformInfo, OriginalFileInfo); if (OriginalFileInfo->cbSize != sizeof(*OriginalFileInfo)) { WARN("incorrect OriginalFileInfo->cbSize of %d\n", OriginalFileInfo->cbSize); SetLastError( ERROR_INVALID_USER_BUFFER ); return FALSE; } OriginalFileInfoW.cbSize = sizeof(OriginalFileInfoW); ret = SetupQueryInfOriginalFileInformationW(InfInformation, InfIndex, AlternativePlatformInfo, &OriginalFileInfoW); if (ret) { WideCharToMultiByte(CP_ACP, 0, OriginalFileInfoW.OriginalInfName, -1, OriginalFileInfo->OriginalInfName, MAX_PATH, NULL, NULL); WideCharToMultiByte(CP_ACP, 0, OriginalFileInfoW.OriginalCatalogName, -1, OriginalFileInfo->OriginalCatalogName, MAX_PATH, NULL, NULL); } return ret; } /*********************************************************************** * SetupQueryInfOriginalFileInformationW (SETUPAPI.@) */ BOOL WINAPI SetupQueryInfOriginalFileInformationW( PSP_INF_INFORMATION InfInformation, UINT InfIndex, PSP_ALTPLATFORM_INFO AlternativePlatformInfo, PSP_ORIGINAL_FILE_INFO_W OriginalFileInfo) { LPCWSTR inf_name; LPCWSTR inf_path; HINF hinf; static const WCHAR wszVersion[] = { 'V','e','r','s','i','o','n',0 }; static const WCHAR wszCatalogFile[] = { 'C','a','t','a','l','o','g','F','i','l','e',0 }; FIXME("(%p, %d, %p, %p): semi-stub\n", InfInformation, InfIndex, AlternativePlatformInfo, OriginalFileInfo); if (OriginalFileInfo->cbSize != sizeof(*OriginalFileInfo)) { WARN("incorrect OriginalFileInfo->cbSize of %d\n", OriginalFileInfo->cbSize); SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } inf_path = (LPWSTR)&InfInformation->VersionData[0]; /* FIXME: we should get OriginalCatalogName from CatalogFile line in * the original inf file and cache it, but that would require building a * .pnf file. */ hinf = SetupOpenInfFileW(inf_path, NULL, INF_STYLE_WIN4, NULL); if (hinf == INVALID_HANDLE_VALUE) return FALSE; if (!SetupGetLineTextW(NULL, hinf, wszVersion, wszCatalogFile, OriginalFileInfo->OriginalCatalogName, sizeof(OriginalFileInfo->OriginalCatalogName)/sizeof(OriginalFileInfo->OriginalCatalogName[0]), NULL)) { OriginalFileInfo->OriginalCatalogName[0] = '\0'; } SetupCloseInfFile(hinf); /* FIXME: not quite correct as we just return the same file name as * destination (copied) inf file, not the source (original) inf file. * to fix it properly would require building a .pnf file */ /* file name is stored in VersionData field of InfInformation */ inf_name = strrchrW(inf_path, '\\'); if (inf_name) inf_name++; else inf_name = inf_path; strcpyW(OriginalFileInfo->OriginalInfName, inf_name); return TRUE; }