wine/dlls/setupapi/setupcab.c
Alex Henrie c293cd781f setupapi: Use CRT functions for memory allocation where possible.
The big win here is getting rid of the reimplementation of wcsdup.
2023-05-25 22:18:57 +02:00

459 lines
13 KiB
C

/*
* 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 <sys/stat.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 malloc(cb);
}
static void CDECL sc_cb_free(void *pv)
{
free(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 %ld, 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 %#lx, 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;
}