/* * Setupapi cabinet routines * * Copyright 2003 Gregory M. Turner * * 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 <string.h> #include <stdlib.h> #include <fcntl.h> #include <share.h> #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "winnls.h" #include "winreg.h" #include "setupapi.h" #include "setupapi_private.h" #include "fdi.h" #include "wine/debug.h" OSVERSIONINFOW OsVersionInfo; HINSTANCE SETUPAPI_hInstance = 0; typedef struct { PSP_FILE_CALLBACK_A msghandler; void *context; char cab_path[MAX_PATH]; char last_cab[MAX_PATH]; char most_recent_target[MAX_PATH]; } SC_HSC_A, *PSC_HSC_A; WINE_DEFAULT_DEBUG_CHANNEL(setupapi); static void * CDECL sc_cb_alloc(ULONG cb) { return HeapAlloc(GetProcessHeap(), 0, cb); } static void CDECL sc_cb_free(void *pv) { HeapFree(GetProcessHeap(), 0, pv); } static INT_PTR CDECL sc_cb_open(char *pszFile, int oflag, int pmode) { DWORD creation = 0, sharing = 0; int ioflag = 0; SECURITY_ATTRIBUTES sa; switch(oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { case _O_RDONLY: ioflag |= GENERIC_READ; break; case _O_WRONLY: ioflag |= GENERIC_WRITE; break; case _O_RDWR: ioflag |= GENERIC_READ | GENERIC_WRITE; break; } if (oflag & _O_CREAT) { if (oflag & _O_EXCL) creation = CREATE_NEW; else if (oflag & _O_TRUNC) creation = CREATE_ALWAYS; else creation = OPEN_ALWAYS; } else { if (oflag & _O_TRUNC) creation = TRUNCATE_EXISTING; else creation = OPEN_EXISTING; } switch( pmode & 0x70 ) { case _SH_DENYRW: sharing = 0L; break; case _SH_DENYWR: sharing = FILE_SHARE_READ; break; case _SH_DENYRD: sharing = FILE_SHARE_WRITE; break; case _SH_COMPAT: case _SH_DENYNO: sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; break; } sa.nLength = sizeof( SECURITY_ATTRIBUTES ); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = !(ioflag & _O_NOINHERIT); return (INT_PTR) CreateFileA(pszFile, ioflag, sharing, &sa, creation, FILE_ATTRIBUTE_NORMAL, NULL); } static UINT CDECL sc_cb_read(INT_PTR hf, void *pv, UINT cb) { DWORD num_read; if (!ReadFile((HANDLE)hf, pv, cb, &num_read, NULL)) return -1; return num_read; } static UINT CDECL sc_cb_write(INT_PTR hf, void *pv, UINT cb) { DWORD num_written; if (!WriteFile((HANDLE)hf, pv, cb, &num_written, NULL)) return -1; return num_written; } static int CDECL sc_cb_close(INT_PTR hf) { if (!CloseHandle((HANDLE)hf)) return -1; return 0; } static LONG CDECL sc_cb_lseek(INT_PTR hf, LONG dist, int seektype) { DWORD ret; if (seektype < 0 || seektype > 2) return -1; if (((ret = SetFilePointer((HANDLE)hf, dist, NULL, seektype)) == INVALID_SET_FILE_POINTER) && GetLastError()) return -1; return ret; } #define SIZEOF_MYSTERIO (MAX_PATH*3) static INT_PTR CDECL sc_FNNOTIFY_A(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) { FILE_IN_CABINET_INFO_A fici; PSC_HSC_A phsc = pfdin->pv; CABINET_INFO_A ci; FILEPATHS_A fp; UINT err; CHAR mysterio[SIZEOF_MYSTERIO]; /* how big? undocumented! probably 256... */ memset(mysterio, 0, SIZEOF_MYSTERIO); switch (fdint) { case fdintCABINET_INFO: TRACE("New cabinet, path %s, set %u, number %u, next disk %s, next cabinet %s.\n", debugstr_a(pfdin->psz3), pfdin->setID, pfdin->iCabinet, debugstr_a(pfdin->psz2), debugstr_a(pfdin->psz1)); ci.CabinetFile = pfdin->psz1; ci.CabinetPath = pfdin->psz3; ci.DiskName = pfdin->psz2; ci.SetId = pfdin->setID; ci.CabinetNumber = pfdin->iCabinet; phsc->msghandler(phsc->context, SPFILENOTIFY_CABINETINFO, (UINT_PTR) &ci, 0); return 0; case fdintPARTIAL_FILE: return 0; case fdintCOPY_FILE: TRACE("Copy file %s, length %d, date %#x, time %#x, attributes %#x.\n", debugstr_a(pfdin->psz1), pfdin->cb, pfdin->date, pfdin->time, pfdin->attribs); fici.NameInCabinet = pfdin->psz1; fici.FileSize = pfdin->cb; fici.Win32Error = 0; fici.DosDate = pfdin->date; fici.DosTime = pfdin->time; fici.DosAttribs = pfdin->attribs; memset(fici.FullTargetName, 0, MAX_PATH); err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEINCABINET, (UINT_PTR)&fici, (UINT_PTR)phsc->last_cab); if (err == FILEOP_DOIT) { TRACE("Callback specified filename: %s\n", debugstr_a(fici.FullTargetName)); if (!fici.FullTargetName[0]) { WARN("Empty return string causing abort.\n"); SetLastError(ERROR_PATH_NOT_FOUND); return -1; } strcpy( phsc->most_recent_target, fici.FullTargetName ); return sc_cb_open(fici.FullTargetName, _O_BINARY | _O_CREAT | _O_WRONLY, _S_IREAD | _S_IWRITE); } else { TRACE("Callback skipped file.\n"); return 0; } case fdintCLOSE_FILE_INFO: TRACE("File extracted.\n"); fp.Source = phsc->last_cab; fp.Target = phsc->most_recent_target; fp.Win32Error = 0; fp.Flags = 0; /* FIXME: set file time and attributes */ sc_cb_close(pfdin->hf); err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEEXTRACTED, (UINT_PTR)&fp, 0); if (err) { SetLastError(err); return FALSE; } else return TRUE; case fdintNEXT_CABINET: TRACE("Need new cabinet, path %s, file %s, disk %s, set %u, number %u.\n", debugstr_a(pfdin->psz3), debugstr_a(pfdin->psz1), debugstr_a(pfdin->psz2), pfdin->setID, pfdin->iCabinet); ci.CabinetFile = pfdin->psz1; ci.CabinetPath = pfdin->psz3; ci.DiskName = pfdin->psz2; ci.SetId = pfdin->setID; ci.CabinetNumber = pfdin->iCabinet; sprintf(phsc->last_cab, "%s%s", phsc->cab_path, ci.CabinetFile); err = phsc->msghandler(phsc->context, SPFILENOTIFY_NEEDNEWCABINET, (UINT_PTR)&ci, (UINT_PTR)mysterio); if (err) { SetLastError(err); return -1; } else { if (mysterio[0]) { /* some easy paranoia. no such carefulness exists on the wide API IIRC */ lstrcpynA(pfdin->psz3, mysterio, SIZEOF_MYSTERIO); } return 0; } default: FIXME("Unknown notification type %d.\n", fdint); return 0; } } /*********************************************************************** * SetupIterateCabinetA (SETUPAPI.@) */ BOOL WINAPI SetupIterateCabinetA(const char *file, DWORD reserved, PSP_FILE_CALLBACK_A callback, void *context) { SC_HSC_A my_hsc; ERF erf; CHAR pszCabinet[MAX_PATH], pszCabPath[MAX_PATH], *filepart = NULL; size_t path_size = 0; const char *p; DWORD fpnsize; HFDI hfdi; BOOL ret; TRACE("file %s, reserved %#x, callback %p, context %p.\n", debugstr_a(file), reserved, callback, context); if (!file) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (strlen(file) >= MAX_PATH) { SetLastError(ERROR_BAD_PATHNAME); return FALSE; } fpnsize = GetFullPathNameA(file, MAX_PATH, pszCabPath, &filepart); if (fpnsize > MAX_PATH) { SetLastError(ERROR_BAD_PATHNAME); return FALSE; } if (filepart) { strcpy(pszCabinet, filepart); *filepart = '\0'; } else { strcpy(pszCabinet, file); pszCabPath[0] = '\0'; } for (p = file; *p; ++p) { if (*p == '/' || *p == '\\') path_size = p - file; } memcpy(my_hsc.cab_path, file, path_size); my_hsc.cab_path[path_size] = 0; TRACE("path: %s, cabfile: %s\n", debugstr_a(pszCabPath), debugstr_a(pszCabinet)); strcpy(my_hsc.last_cab, file); my_hsc.msghandler = callback; my_hsc.context = context; hfdi = FDICreate(sc_cb_alloc, sc_cb_free, sc_cb_open, sc_cb_read, sc_cb_write, sc_cb_close, sc_cb_lseek, cpuUNKNOWN, &erf); if (!hfdi) return FALSE; ret = FDICopy(hfdi, pszCabinet, pszCabPath, 0, sc_FNNOTIFY_A, NULL, &my_hsc); FDIDestroy(hfdi); return ret; } struct iterate_wtoa_ctx { PSP_FILE_CALLBACK_A orig_cb; void *orig_ctx; }; static UINT WINAPI iterate_wtoa_cb(void *pctx, UINT message, UINT_PTR param1, UINT_PTR param2) { struct iterate_wtoa_ctx *ctx = pctx; switch (message) { case SPFILENOTIFY_CABINETINFO: case SPFILENOTIFY_NEEDNEWCABINET: { const CABINET_INFO_A *infoA = (const CABINET_INFO_A *)param1; WCHAR pathW[MAX_PATH], fileW[MAX_PATH], diskW[MAX_PATH]; CABINET_INFO_W infoW = { .CabinetPath = pathW, .CabinetFile = fileW, .DiskName = diskW, .SetId = infoA->SetId, .CabinetNumber = infoA->CabinetNumber, }; MultiByteToWideChar(CP_ACP, 0, infoA->CabinetPath, -1, pathW, ARRAY_SIZE(pathW)); MultiByteToWideChar(CP_ACP, 0, infoA->CabinetFile, -1, fileW, ARRAY_SIZE(fileW)); MultiByteToWideChar(CP_ACP, 0, infoA->DiskName, -1, diskW, ARRAY_SIZE(diskW)); if (message == SPFILENOTIFY_CABINETINFO) return ctx->orig_cb(ctx->orig_ctx, message, (UINT_PTR)&infoW, 0); else { char *newpathA = (char *)param2; WCHAR newpathW[MAX_PATH] = {0}; BOOL ret = ctx->orig_cb(ctx->orig_ctx, message, (UINT_PTR)&infoW, (UINT_PTR)newpathW); WideCharToMultiByte(CP_ACP, 0, newpathW, -1, newpathA, MAX_PATH, NULL, NULL); return ret; } } case SPFILENOTIFY_FILEINCABINET: { FILE_IN_CABINET_INFO_A *infoA = (FILE_IN_CABINET_INFO_A *)param1; const char *cabA = (const char *)param2; WCHAR cabW[MAX_PATH], fileW[MAX_PATH]; FILE_IN_CABINET_INFO_W infoW = { .NameInCabinet = fileW, .FileSize = infoA->FileSize, .Win32Error = infoA->Win32Error, .DosDate = infoA->DosDate, .DosTime = infoA->DosTime, .DosAttribs = infoA->DosAttribs, }; BOOL ret; MultiByteToWideChar(CP_ACP, 0, infoA->NameInCabinet, -1, fileW, ARRAY_SIZE(fileW)); MultiByteToWideChar(CP_ACP, 0, cabA, -1, cabW, ARRAY_SIZE(cabW)); ret = ctx->orig_cb(ctx->orig_ctx, message, (UINT_PTR)&infoW, (UINT_PTR)cabW); WideCharToMultiByte(CP_ACP, 0, infoW.FullTargetName, -1, infoA->FullTargetName, ARRAY_SIZE(infoA->FullTargetName), NULL, NULL); return ret; } case SPFILENOTIFY_FILEEXTRACTED: { const FILEPATHS_A *pathsA = (const FILEPATHS_A *)param1; WCHAR targetW[MAX_PATH], sourceW[MAX_PATH]; FILEPATHS_W pathsW = { .Target = targetW, .Source = sourceW, .Win32Error = pathsA->Win32Error, .Flags = pathsA->Flags, }; MultiByteToWideChar(CP_ACP, 0, pathsA->Target, -1, targetW, ARRAY_SIZE(targetW)); MultiByteToWideChar(CP_ACP, 0, pathsA->Source, -1, sourceW, ARRAY_SIZE(sourceW)); return ctx->orig_cb(ctx->orig_ctx, message, (UINT_PTR)&pathsW, 0); } default: FIXME("Unexpected callback %#x.\n", message); return FALSE; } } /*********************************************************************** * SetupIterateCabinetW (SETUPAPI.@) */ BOOL WINAPI SetupIterateCabinetW(const WCHAR *fileW, DWORD reserved, PSP_FILE_CALLBACK_W handler, void *context) { struct iterate_wtoa_ctx ctx = {handler, context}; char fileA[MAX_PATH]; if (!WideCharToMultiByte(CP_ACP, 0, fileW, -1, fileA, ARRAY_SIZE(fileA), NULL, NULL)) return FALSE; return SetupIterateCabinetA(fileA, reserved, iterate_wtoa_cb, &ctx); } /*********************************************************************** * DllMain * * PARAMS * hinstDLL [I] handle to the DLL's instance * fdwReason [I] * lpvReserved [I] reserved, must be NULL * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hinstDLL); OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); if (!GetVersionExW(&OsVersionInfo)) return FALSE; SETUPAPI_hInstance = hinstDLL; break; case DLL_PROCESS_DETACH: if (lpvReserved) break; SetupCloseLog(); break; } return TRUE; }