/*-
* Copyright 2003,2004 Colin Percival
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Changelog:
* 2005-04-26 - Define the header as a C structure, add a CRC32 checksum to
* the header, and make all the types 32-bit.
* --Benjamin Smedberg <[email protected]>
* 2007-11-14 - Added CalculateCrc() and ApplyBinaryPatch() methods.
* --Rahul Kuchhal
* 2016-07-27 - Improve validation of diffs.
* --Ricky Zhou
*/
#include "mbspatch.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <limits.h>
#ifdef _WIN32
# include <io.h>
# include <winsock2.h>
#else
# include <unistd.h>
# include <arpa/inet.h>
#endif
extern "C" {
#include <7zCrc.h>
}
#ifndef SSIZE_MAX
# define SSIZE_MAX LONG_MAX
#endif
int
MBS_ReadHeader(int fd, MBSPatchHeader *header)
{
int s = read(fd, header, sizeof(MBSPatchHeader));
if (s != sizeof(MBSPatchHeader))
return READ_ERROR;
header->slen = ntohl(header->slen);
header->scrc32 = ntohl(header->scrc32);
header->dlen = ntohl(header->dlen);
header->cblen = ntohl(header->cblen);
header->difflen = ntohl(header->difflen);
header->extralen = ntohl(header->extralen);
struct stat hs;
s = fstat(fd, &hs);
if (s != 0)
return READ_ERROR;
if (memcmp(header->tag, "MBDIFF10", 8) != 0)
return UNEXPECTED_ERROR;
if (hs.st_size > INT_MAX)
return UNEXPECTED_ERROR;
size_t size = static_cast<size_t>(hs.st_size);
if (size < sizeof(MBSPatchHeader))
return UNEXPECTED_ERROR;
size -= sizeof(MBSPatchHeader);
if (size < header->cblen)
return UNEXPECTED_ERROR;
size -= header->cblen;
if (size < header->difflen)
return UNEXPECTED_ERROR;
size -= header->difflen;
if (size < header->extralen)
return UNEXPECTED_ERROR;
size -= header->extralen;
if (size != 0)
return UNEXPECTED_ERROR;
return OK;
}
int
MBS_ApplyPatch(const MBSPatchHeader *header, int patchfd,
unsigned char *fbuffer, int filefd)
{
unsigned char *fbufstart = fbuffer;
unsigned char *fbufend = fbuffer + header->slen;
unsigned char *buf = (unsigned char*) malloc(header->cblen +
header->difflen +
header->extralen);
if (!buf)
return MEM_ERROR;
int rv = OK;
int r = header->cblen + header->difflen + header->extralen;
unsigned char *wb = buf;
while (r) {
int c = read(patchfd, wb, (r > SSIZE_MAX) ? SSIZE_MAX : r);
if (c < 0) {
rv = READ_ERROR;
goto end;
}
r -= c;
wb += c;
if (c == 0 && r) {
rv = UNEXPECTED_ERROR;
goto end;
}
}
{
MBSPatchTriple *ctrlsrc = (MBSPatchTriple*) buf;
if (header->cblen % sizeof(MBSPatchTriple) != 0) {
rv = UNEXPECTED_ERROR;
goto end;
}
unsigned char *diffsrc = buf + header->cblen;
unsigned char *extrasrc = diffsrc + header->difflen;
MBSPatchTriple *ctrlend = (MBSPatchTriple*) diffsrc;
unsigned char *diffend = extrasrc;
unsigned char *extraend = extrasrc + header->extralen;
while (ctrlsrc < ctrlend) {
ctrlsrc->x = ntohl(ctrlsrc->x);
ctrlsrc->y = ntohl(ctrlsrc->y);
ctrlsrc->z = ntohl(ctrlsrc->z);
#ifdef DEBUG_bsmedberg
printf("Applying block:\n"
" x: %u\n"
" y: %u\n"
" z: %i\n",
ctrlsrc->x,
ctrlsrc->y,
ctrlsrc->z);
#endif
/* Add x bytes from oldfile to x bytes from the diff block */
if (ctrlsrc->x > static_cast<size_t>(fbufend - fbuffer) ||
ctrlsrc->x > static_cast<size_t>(diffend - diffsrc)) {
rv = UNEXPECTED_ERROR;
goto end;
}
for (unsigned int i = 0; i < ctrlsrc->x; ++i) {
diffsrc[i] += fbuffer[i];
}
if ((int) write(filefd, diffsrc, ctrlsrc->x) != ctrlsrc->x) {
rv = WRITE_ERROR;
goto end;
}
fbuffer += ctrlsrc->x;
diffsrc += ctrlsrc->x;
/* Copy y bytes from the extra block */
if (ctrlsrc->y > static_cast<size_t>(extraend - extrasrc)) {
rv = UNEXPECTED_ERROR;
goto end;
}
if ((int) write(filefd, extrasrc, ctrlsrc->y) != ctrlsrc->y) {
rv = WRITE_ERROR;
goto end;
}
extrasrc += ctrlsrc->y;
/* "seek" forwards in oldfile by z bytes */
if (ctrlsrc->z < fbufstart - fbuffer ||
ctrlsrc->z > fbufend - fbuffer) {
rv = UNEXPECTED_ERROR;
goto end;
}
fbuffer += ctrlsrc->z;
/* and on to the next control block */
++ctrlsrc;
}
}
end:
free(buf);
return rv;
}
int CalculateCrc(const unsigned char *buf, int size) {
CrcGenerateTable();
unsigned int crc = 0xffffffffL;
crc = ~CrcCalc(buf, size);
return crc;
}
/* _O_BINARY is a MSWindows open() mode flag. When absent, MSWindows
* open() translates CR+LF to LF; when present, it passes bytes
* through faithfully. Under *nix, we are always in binary mode, so
* the following #define turns this flag into a no-op w.r.t. bitwise
* OR. Note that this would be DANGEROUS AND UNSOUND if we used
* _O_BINARY other than as a bitwise OR mask (e.g., as a bitwise AND
* mask to check for binary mode), but it seems OK in the limited
* context of the following small function. */
#ifndef _O_BINARY
# define _O_BINARY 0
#endif
int ApplyBinaryPatch(const wchar_t *old_file, const wchar_t *patch_file,
const wchar_t *new_file) {
int ret = OK;
int ofd = -1;
int nfd = -1;
unsigned char *buf = NULL;
int pfd = _wopen(patch_file, O_RDONLY | _O_BINARY);
if (pfd < 0) return READ_ERROR;
do {
MBSPatchHeader header;
if ((ret = MBS_ReadHeader(pfd, &header)))
break;
ofd = _wopen(old_file, O_RDONLY | _O_BINARY);
if (ofd < 0) {
ret = READ_ERROR;
break;
}
struct stat os;
if ((ret = fstat(ofd, &os))) {
ret = READ_ERROR;
break;
}
if (os.st_size != header.slen) {
ret = UNEXPECTED_ERROR;
break;
}
buf = (unsigned char*) malloc(header.slen);
if (!buf) {
ret = MEM_ERROR;
break;
}
if (read(ofd, buf, header.slen) != header.slen) {
ret = READ_ERROR;
break;
}
if (CalculateCrc(buf, header.slen) != header.scrc32) {
ret = CRC_ERROR;
break;
}
nfd = _wopen(new_file, O_WRONLY | O_TRUNC | O_CREAT | _O_BINARY);
if (nfd < 0) {
ret = READ_ERROR;
break;
}
ret = MBS_ApplyPatch(&header, pfd, buf, nfd);
} while (0);
free(buf);
close(pfd);
if (ofd >= 0) close(ofd);
if (nfd >= 0) close(nfd);
return ret;
}