NSIS/Source/exehead/fileform.c
kichik 2efd32160b fixed bug #1874297 - Error decompressing data! Corrupted installer?
problem was that decompression ended prematurely. instead of waiting for zlib to return Z_STREAM_END, it checked if no more output was required. this could cause a case where zlib wouldn't read its entire stream. in this particular case, the compressed headers were 0x4001 bytes long and compressed data input buffer size is only 0x4000. instead of waiting for another run of inflate() to read the last byte, which is probably eof, _dodecmop() broke the loop and didn't read that extra byte. since loadHeaders() relays on _dodecomp() to read the entire header so it can calculate where the data block begins, all reads from the data block suddenly became off by one, causing "decompression errors".

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@5557 212acab6-be3b-0410-9dea-997c60f758d6
2008-02-23 16:28:23 +00:00

575 lines
14 KiB
C

/*
* fileform.c
*
* This file is a part of NSIS.
*
* Copyright (C) 1999-2008 Nullsoft and Contributors
*
* Licensed under the zlib/libpng license (the "License");
* you may not use this file except in compliance with the License.
*
* Licence details can be found in the file COPYING.
*
* This software is provided 'as-is', without any express or implied
* warranty.
*/
#include "../Platform.h"
#include "fileform.h"
#include "util.h"
#include "state.h"
#include "resource.h"
#include "lang.h"
#include "ui.h"
#include "exec.h"
#include "../crc32.h"
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
#ifdef NSIS_COMPRESS_USE_ZLIB
#include "../zlib/ZLIB.H"
#endif
#ifdef NSIS_COMPRESS_USE_LZMA
#include "../7zip/LZMADecode.h"
#define z_stream lzma_stream
#define inflateInit(x) lzmaInit(x)
#define inflateReset(x) lzmaInit(x)
#define inflate(x) lzmaDecode(x)
#define Z_OK LZMA_OK
#define Z_STREAM_END LZMA_STREAM_END
#endif
#ifdef NSIS_COMPRESS_USE_BZIP2
#include "../bzip2/bzlib.h"
#define z_stream DState
#define inflateInit(x) BZ2_bzDecompressInit(x)
#define inflateReset(x) BZ2_bzDecompressInit(x)
#define inflate(x) BZ2_bzDecompress(x)
#define Z_OK BZ_OK
#define Z_STREAM_END BZ_STREAM_END
#endif//NSIS_COMPRESS_USE_BZIP2
#endif//NSIS_CONFIG_COMPRESSION_SUPPORT
struct block_header g_blocks[BLOCKS_NUM];
header *g_header;
int g_flags;
int g_filehdrsize;
int g_is_uninstaller;
HANDLE g_db_hFile=INVALID_HANDLE_VALUE;
#if defined(NSIS_CONFIG_COMPRESSION_SUPPORT) && defined(NSIS_COMPRESS_WHOLE)
HANDLE dbd_hFile=INVALID_HANDLE_VALUE;
static int dbd_size, dbd_pos, dbd_srcpos, dbd_fulllen;
#endif//NSIS_COMPRESS_WHOLE
static int m_length;
static int m_pos;
#define _calc_percent() (MulDiv(min(m_pos,m_length),100,m_length))
#ifdef NSIS_COMPRESS_WHOLE
static int NSISCALL calc_percent()
{
return _calc_percent();
}
#else
#define calc_percent() _calc_percent()
#endif
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
#if defined(NSIS_CONFIG_CRC_SUPPORT) || defined(NSIS_COMPRESS_WHOLE)
BOOL CALLBACK verProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_INITDIALOG)
{
SetTimer(hwndDlg,1,250,NULL);
uMsg = WM_TIMER;
}
if (uMsg == WM_TIMER)
{
char bt[64];
int percent=calc_percent();
#ifdef NSIS_COMPRESS_WHOLE
char *msg=g_header?_LANG_UNPACKING:_LANG_VERIFYINGINST;
#else
char *msg=_LANG_VERIFYINGINST;
#endif
wsprintf(bt,msg,percent);
my_SetWindowText(hwndDlg,bt);
my_SetDialogItemText(hwndDlg,IDC_STR,bt);
}
return 0;
}
DWORD verify_time;
void handle_ver_dlg(BOOL kill)
{
static HWND hwnd;
if (kill)
{
if (hwnd) DestroyWindow(hwnd);
hwnd = NULL;
return;
}
if (hwnd)
{
MessageLoop(0);
}
else if (GetTickCount() > verify_time)
{
#ifdef NSIS_COMPRESS_WHOLE
if (g_hwnd)
{
if (g_exec_flags.status_update & 1)
{
char bt[64];
wsprintf(bt, "... %d%%", calc_percent());
update_status_text(0, bt);
}
}
else
#endif
{
hwnd = CreateDialog(
g_hInstance,
MAKEINTRESOURCE(IDD_VERIFY),
0,
verProc
);
}
}
}
#endif//NSIS_CONFIG_CRC_SUPPORT || NSIS_COMPRESS_WHOLE
#endif//NSIS_CONFIG_VISIBLE_SUPPORT
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
static z_stream g_inflate_stream;
#endif
const char * NSISCALL loadHeaders(int cl_flags)
{
int left;
#ifdef NSIS_CONFIG_CRC_SUPPORT
crc32_t crc = 0;
int do_crc = 0;
#endif//NSIS_CONFIG_CRC_SUPPORT
void *data;
firstheader h;
header *header;
HANDLE db_hFile;
#ifdef NSIS_CONFIG_CRC_SUPPORT
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
verify_time = GetTickCount() + 1000;
#endif
#endif//NSIS_CONFIG_CRC_SUPPORT
GetModuleFileName(NULL, state_exe_path, NSIS_MAX_STRLEN);
g_db_hFile = db_hFile = myOpenFile(state_exe_path, GENERIC_READ, OPEN_EXISTING);
if (db_hFile == INVALID_HANDLE_VALUE)
{
return _LANG_CANTOPENSELF;
}
mystrcpy(state_exe_directory, state_exe_path);
mystrcpy(state_exe_file, trimslashtoend(state_exe_directory));
left = m_length = GetFileSize(db_hFile,NULL);
while (left > 0)
{
static char temp[32768];
DWORD l = min(left, (g_filehdrsize ? 32768 : 512));
if (!ReadSelfFile(temp, l))
{
#if defined(NSIS_CONFIG_CRC_SUPPORT) && defined(NSIS_CONFIG_VISIBLE_SUPPORT)
handle_ver_dlg(TRUE);
#endif//NSIS_CONFIG_CRC_SUPPORT
return _LANG_INVALIDCRC;
}
if (!g_filehdrsize)
{
mini_memcpy(&h, temp, sizeof(firstheader));
if (
(h.flags & (~FH_FLAGS_MASK)) == 0 &&
h.siginfo == FH_SIG &&
h.nsinst[2] == FH_INT3 &&
h.nsinst[1] == FH_INT2 &&
h.nsinst[0] == FH_INT1
)
{
g_filehdrsize = m_pos;
#if defined(NSIS_CONFIG_CRC_SUPPORT) || defined(NSIS_CONFIG_SILENT_SUPPORT)
cl_flags |= h.flags;
#endif
#ifdef NSIS_CONFIG_SILENT_SUPPORT
g_exec_flags.silent |= cl_flags & FH_FLAGS_SILENT;
#endif
if (h.length_of_all_following_data > left)
return _LANG_INVALIDCRC;
#ifdef NSIS_CONFIG_CRC_SUPPORT
if ((cl_flags & FH_FLAGS_FORCE_CRC) == 0)
{
if (cl_flags & FH_FLAGS_NO_CRC)
break;
}
do_crc++;
#ifndef NSIS_CONFIG_CRC_ANAL
left = h.length_of_all_following_data - 4;
// end crc checking at crc :) this means you can tack shit on the end and it'll still work.
#else //!NSIS_CONFIG_CRC_ANAL
left -= 4;
#endif//NSIS_CONFIG_CRC_ANAL
// this is in case the end part is < 512 bytes.
if (l > (DWORD)left) l=(DWORD)left;
#else//!NSIS_CONFIG_CRC_SUPPORT
// no crc support, no need to keep on reading
break;
#endif//!NSIS_CONFIG_CRC_SUPPORT
}
}
#ifdef NSIS_CONFIG_CRC_SUPPORT
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
#ifdef NSIS_CONFIG_SILENT_SUPPORT
else if ((cl_flags & FH_FLAGS_SILENT) == 0)
#endif//NSIS_CONFIG_SILENT_SUPPORT
{
handle_ver_dlg(FALSE);
}
#endif//NSIS_CONFIG_VISIBLE_SUPPORT
#ifndef NSIS_CONFIG_CRC_ANAL
if (left < m_length)
#endif//NSIS_CONFIG_CRC_ANAL
crc = CRC32(crc, temp, l);
#endif//NSIS_CONFIG_CRC_SUPPORT
m_pos += l;
left -= l;
}
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
#ifdef NSIS_CONFIG_CRC_SUPPORT
handle_ver_dlg(TRUE);
#endif//NSIS_CONFIG_CRC_SUPPORT
#endif//NSIS_CONFIG_VISIBLE_SUPPORT
if (!g_filehdrsize)
return _LANG_INVALIDCRC;
#ifdef NSIS_CONFIG_CRC_SUPPORT
if (do_crc)
{
crc32_t fcrc;
SetSelfFilePointer(m_pos);
if (!ReadSelfFile(&fcrc, sizeof(crc32_t)) || crc != fcrc)
return _LANG_INVALIDCRC;
}
#endif//NSIS_CONFIG_CRC_SUPPORT
data = (void *)GlobalAlloc(GPTR,h.length_of_header);
#ifdef NSIS_COMPRESS_WHOLE
inflateReset(&g_inflate_stream);
{
char fno[MAX_PATH];
my_GetTempFileName(fno, state_temp_dir);
dbd_hFile=CreateFile(fno,GENERIC_WRITE|GENERIC_READ,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE,NULL);
if (dbd_hFile == INVALID_HANDLE_VALUE)
return _LANG_ERRORWRITINGTEMP;
}
dbd_srcpos = SetSelfFilePointer(g_filehdrsize + sizeof(firstheader));
#ifdef NSIS_CONFIG_CRC_SUPPORT
dbd_fulllen = dbd_srcpos - sizeof(h) + h.length_of_all_following_data - ((h.flags & FH_FLAGS_NO_CRC) ? 0 : sizeof(crc32_t));
#else
dbd_fulllen = dbd_srcpos - sizeof(h) + h.length_of_all_following_data;
#endif//NSIS_CONFIG_CRC_SUPPORT
#else
SetSelfFilePointer(g_filehdrsize + sizeof(firstheader));
#endif//NSIS_COMPRESS_WHOLE
if (GetCompressedDataFromDataBlockToMemory(-1, data, h.length_of_header) != h.length_of_header)
{
return _LANG_INVALIDCRC;
}
header = g_header = data;
g_flags = header->flags;
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
if (h.flags & FH_FLAGS_UNINSTALL)
g_is_uninstaller++;
#endif
// set offsets to real memory offsets rather than installer's header offset
left = BLOCKS_NUM;
while (left--)
header->blocks[left].offset += (int)data;
#ifdef NSIS_COMPRESS_WHOLE
header->blocks[NB_DATA].offset = dbd_pos;
#else
header->blocks[NB_DATA].offset = SetFilePointer(db_hFile,0,NULL,FILE_CURRENT);
#endif
mini_memcpy(&g_blocks, &header->blocks, sizeof(g_blocks));
return 0;
}
#define IBUFSIZE 16384
#define OBUFSIZE 32768
// returns -3 if compression error/eof/etc
#if !defined(NSIS_COMPRESS_WHOLE) || !defined(NSIS_CONFIG_COMPRESSION_SUPPORT)
int NSISCALL _dodecomp(int offset, HANDLE hFileOut, char *outbuf, int outbuflen)
{
static char inbuffer[IBUFSIZE+OBUFSIZE];
char *outbuffer;
int outbuffer_len=outbuf?outbuflen:OBUFSIZE;
int retval=0;
int input_len;
outbuffer = outbuf?outbuf:(inbuffer+IBUFSIZE);
if (offset>=0)
{
SetSelfFilePointer(g_blocks[NB_DATA].offset+offset);
}
if (!ReadSelfFile((LPVOID)&input_len,sizeof(int))) return -3;
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
if (input_len & 0x80000000) // compressed
{
char progress[64];
int input_len_total;
DWORD ltc = GetTickCount(), tc;
inflateReset(&g_inflate_stream);
input_len_total = input_len &= 0x7fffffff; // take off top bit.
while (input_len > 0)
{
int l=min(input_len,IBUFSIZE);
int err;
if (!ReadSelfFile((LPVOID)inbuffer,l))
return -3;
g_inflate_stream.next_in = inbuffer;
g_inflate_stream.avail_in = l;
input_len-=l;
for (;;)
{
int u;
g_inflate_stream.next_out = outbuffer;
g_inflate_stream.avail_out = (unsigned int)outbuffer_len;
err=inflate(&g_inflate_stream);
if (err<0) return -4;
u=(char*)g_inflate_stream.next_out - outbuffer;
tc = GetTickCount();
if (g_exec_flags.status_update & 1 && (tc - ltc > 200 || !input_len))
{
wsprintf(progress, "... %d%%", MulDiv(input_len_total - input_len, 100, input_len_total));
update_status_text(0, progress);
ltc = tc;
}
// if there's no output, more input is needed
if (!u)
break;
if (!outbuf)
{
DWORD r;
if (!WriteFile(hFileOut,outbuffer,u,&r,NULL) || (int)r != u) return -2;
retval+=u;
}
else
{
retval+=u;
outbuffer_len-=u;
outbuffer=g_inflate_stream.next_out;
}
if (err==Z_STREAM_END) return retval;
}
}
}
else
#endif//NSIS_CONFIG_COMPRESSION_SUPPORT
{
if (!outbuf)
{
while (input_len > 0)
{
DWORD l=min(input_len,outbuffer_len);
DWORD t;
if (!ReadSelfFile((LPVOID)inbuffer,l)) return -3;
if (!WriteFile(hFileOut,inbuffer,l,&t,NULL) || l!=t) return -2;
retval+=l;
input_len-=l;
}
}
else
{
int l=min(input_len,outbuflen);
if (!ReadSelfFile((LPVOID)outbuf,l)) return -3;
retval=l;
}
}
return retval;
}
#else//NSIS_COMPRESS_WHOLE
static char _inbuffer[IBUFSIZE];
static char _outbuffer[OBUFSIZE];
extern int m_length;
extern int m_pos;
extern BOOL CALLBACK verProc(HWND, UINT, WPARAM, LPARAM);
extern BOOL CALLBACK DialogProc(HWND, UINT, WPARAM, LPARAM);
static int NSISCALL __ensuredata(int amount)
{
int needed=amount-(dbd_size-dbd_pos);
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
verify_time=GetTickCount()+500;
#endif
if (needed>0)
{
SetSelfFilePointer(dbd_srcpos);
SetFilePointer(dbd_hFile,dbd_size,NULL,FILE_BEGIN);
m_length=needed;
m_pos=0;
for (;;)
{
int err;
int l=min(IBUFSIZE,dbd_fulllen-dbd_srcpos);
if (!ReadSelfFile((LPVOID)_inbuffer,l)) return -1;
dbd_srcpos+=l;
g_inflate_stream.next_in=_inbuffer;
g_inflate_stream.avail_in=l;
do
{
DWORD r,t;
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
if (g_header)
#ifdef NSIS_CONFIG_SILENT_SUPPORT
if (!g_exec_flags.silent)
#endif
{
m_pos=m_length-(amount-(dbd_size-dbd_pos));
handle_ver_dlg(FALSE);
}
#endif//NSIS_CONFIG_VISIBLE_SUPPORT
g_inflate_stream.next_out=_outbuffer;
g_inflate_stream.avail_out=OBUFSIZE;
err=inflate(&g_inflate_stream);
if (err<0)
{
return -3;
}
r=(DWORD)g_inflate_stream.next_out-(DWORD)_outbuffer;
if (r)
{
if (!WriteFile(dbd_hFile,_outbuffer,r,&t,NULL) || r != t)
{
return -2;
}
dbd_size+=r;
}
else if (g_inflate_stream.avail_in || !l) return -3;
else break;
}
while (g_inflate_stream.avail_in);
if (amount-(dbd_size-dbd_pos) <= 0) break;
}
SetFilePointer(dbd_hFile,dbd_pos,NULL,FILE_BEGIN);
}
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
handle_ver_dlg(TRUE);
#endif//NSIS_CONFIG_VISIBLE_SUPPORT
return 0;
}
int NSISCALL _dodecomp(int offset, HANDLE hFileOut, char *outbuf, int outbuflen)
{
DWORD r;
int input_len;
int retval;
if (offset>=0)
{
dbd_pos=g_blocks[NB_DATA].offset+offset;
SetFilePointer(dbd_hFile,dbd_pos,NULL,FILE_BEGIN);
}
retval=__ensuredata(sizeof(int));
if (retval<0) return retval;
if (!ReadFile(dbd_hFile,(LPVOID)&input_len,sizeof(int),&r,NULL) || r!=sizeof(int)) return -3;
dbd_pos+=sizeof(int);
retval=__ensuredata(input_len);
if (retval < 0) return retval;
if (!outbuf)
{
while (input_len > 0)
{
DWORD t;
DWORD l=min(input_len,IBUFSIZE);
if (!ReadFile(dbd_hFile,(LPVOID)_inbuffer,l,&r,NULL) || l != r) return -3;
if (!WriteFile(hFileOut,_inbuffer,r,&t,NULL) || t != l) return -2;
retval+=r;
input_len-=r;
dbd_pos+=r;
}
}
else
{
if (!ReadFile(dbd_hFile,(LPVOID)outbuf,min(input_len,outbuflen),&r,NULL)) return -3;
retval=r;
dbd_pos+=r;
}
return retval;
}
#endif//NSIS_COMPRESS_WHOLE
BOOL NSISCALL ReadSelfFile(LPVOID lpBuffer, DWORD nNumberOfBytesToRead)
{
DWORD rd;
return ReadFile(g_db_hFile,lpBuffer,nNumberOfBytesToRead,&rd,NULL) && (rd == nNumberOfBytesToRead);
}
DWORD NSISCALL SetSelfFilePointer(LONG lDistanceToMove)
{
return SetFilePointer(g_db_hFile,lDistanceToMove,NULL,FILE_BEGIN);
}