File:  [ELWIX - Embedded LightWeight unIX -] / libaitsync / src / aitsync.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 24 16:00:15 2010 UTC (14 years, 4 months ago) by misho
Branches: misho
CVS tags: sync1_0, start
libaitsync Delta patch library

/*************************************************************************
* (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
*  by Michael Pounov <misho@openbsd-bg.org>
*
* $Author: misho $
* $Id: aitsync.c,v 1.1.1.1 2010/03/24 16:00:15 misho Exp $
*
*************************************************************************/
#include "global.h"
#include "tool.h"
#include "zc.h"
#include "patch.h"
#include "file.h"


static int sync_Errno;
static char sync_Error[STRSIZ];


static inline int func_comp(sync_tag_t const *t1, sync_tag_t const *t2)
{
	return t1->st_tag - t2->st_tag;
}

//
// Error maintenance functions ...
//

// sync_GetErrno() Get error code of last operation
inline int sync_GetErrno()
{
	return sync_Errno;
}

// sync_GetError() Get error text of last operation
inline const char *sync_GetError()
{
	return sync_Error;
}

// sync_SetErr() Set error to variables for internal use!!!
inline void syncSetErr(int eno, char *estr, ...)
{
	va_list lst;

	sync_Errno = eno;
	memset(sync_Error, 0, STRSIZ);
	va_start(lst, estr);
	vsnprintf(sync_Error, STRSIZ, estr, lst);
	va_end(lst);
}

// ----------------------------------------------------------

/*
 * syncSignature() Calculate and create signature for diff
 * @csInput = Input patched file name for calculating check sums
 * @csSig = Output Signature file name
 * return: -1 error, 0 ok
 */
int syncSignature(const char *csInput, const char *csSig)
{
	int inf, outf, ret;
	u_char buf[CHUNK_MAX];
	register int i = 0;
	off_t off = 0ll;
	sync_chunk_t sc;

	inf = syncOpen(csInput, O_RDONLY);
	if (inf == -1)
		return inf;
	outf = syncOpen(csSig, O_WRONLY);
	if (outf == -1) {
		syncClose(inf);
		return outf;
	}

	for (i = 0, off = 0ll, ret = -1; ret; i++, off += ret) {
		memset(buf, 0, CHUNK_MAX);
		ret = read(inf, buf, CHUNK_MAX);
		if (ret == -1) {
			SETERR;
			break;
		}

		// fill chunk
		sync_mksig(i, off, buf, ret, &sc);

		if (write(outf, &sc, sizeof sc) == -1) {
			SETERR;
			break;
		}
	}

	syncClose(outf);
	syncClose(inf);
	return ret;
}

/*
 * syncDelta() Create Delta patch file
 * @csInput = Input original source file name for make delta patch file
 * @csSig = Input Signature file name
 * @csDelta = Output Delta patch file name
 * @compress = Compress output, 0 not compressed
 * return: -1 error, 0 ok
 */
int syncDelta(const char *csInput, const char *csSig, const char *csDelta, int compress)
{
	int inf, outf, f, sigf, ret, cnt;
	size_t blk;
	register int i, j, c, cx;
	struct stat sb, sb_f;
	u_long tags[TABLESIZ];
	sync_tag_t *tag_table;
	sync_chunk_t *chunks, *find, sc;
	u_char buf[CHUNK_MAX];
	off_t off;
	char szTemp[MAXPATHLEN];

	/* load signatures */

	sigf = syncOpen(csSig, O_RDONLY);
	if (sigf == -1) {
		return sigf;
	}
	if (fstat(sigf, &sb) == -1) {
		SETERR;
		syncClose(sigf);
		return -1;
	} else {
		if (!sb.st_size) {
			syncClose(sigf);
			return 1;
		}

		cnt = sb.st_size / sizeof(sync_chunk_t);
		if (sb.st_size % sizeof(sync_chunk_t)) {
			syncSetErr(ENOEXEC, "Error:: signature file is broken!\n");
			syncClose(sigf);
			return -1;
		}
	}
	chunks = (sync_chunk_t*) mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, sigf, 0);
	if (MAP_FAILED == chunks) {
		SETERR;
		syncClose(sigf);
		return -1;
	}
	syncClose(sigf);

	/* build from signatures sorted index and hashes */

	// init tags array
	for (i = 0; i < TABLESIZ; i++)
		tags[i] = NULL_TAG;

	// build index from signature blocks
	tag_table = (sync_tag_t*) calloc(cnt, sizeof(sync_tag_t));
	if (!tag_table) {
		SETERR;
		munmap(chunks, sb.st_size);
		return -1;
	} else {
		for (i = 0; i < cnt; i++) {
			tag_table[i].st_id = i;
			tag_table[i].st_tag = GETTAG(chunks[i].sc_roll);
		}

		qsort(tag_table, cnt, sizeof(sync_tag_t), (int (*)(const void *, const void *)) func_comp);
	}
	// assign less id position in tag_table to tags
	for (i = cnt - 1; i > -1; i--)
		tags[tag_table[i].st_tag] = i;

	/* build delta patch */

	inf = syncOpen(csInput, O_RDONLY);
	if (inf == -1) {
		free(tag_table);
		munmap(chunks, sb.st_size);
		return inf;
	}
	if (compress)
		f = syncTemp(szTemp, MAXPATHLEN);
	else
		f = syncOpen(csDelta, O_WRONLY);
	if (f == -1) {
		syncClose(inf);
		free(tag_table);
		munmap(chunks, sb.st_size);
		return f;
	}

	for (i = 0, off = 0ll, ret = -1, blk = 0; (ret = read(inf, buf, CHUNK_MAX)); i++, off += ret) {
		if (ret == -1) {
			SETERR;
			break;
		}
		find = NULL;

		// printf("+ find=%p off=%llu i=%d blk=%d\n", find, off, i, blk);

		// check chunk for differences with signature
		sync_mksig(i, off, buf, ret, &sc);
		cx = GETTAG(sc.sc_roll);
		// find in hash -> hash_sorted_table
		if (NULL_TAG != tags[cx] && tag_table[tags[cx]].st_tag == cx) {
			// find in hash_sorted_table crc == -> real chunks id
			for (j = 0, c = tag_table[tags[cx]].st_id; tag_table[tags[cx] + j].st_tag == cx; 
					j++, c = tag_table[tags[cx] + j].st_id) {
				if (chunks[c].sc_magic == sc.sc_magic && chunks[c].sc_len == sc.sc_len && 
						chunks[c].sc_roll == sc.sc_roll && 
						!memcmp(chunks[c].sc_cksum, sc.sc_cksum, MD5_DIGEST_LENGTH)) {
					find = &chunks[c];
					break;
				}
			}
		}

		// printf("+ find=%p off=%llu i=%d blk=%d\n", find, off, i, blk);

		// if match chunk, check for previous match
		if (!blk && find)
			continue;
		// if not find chunk in signature skip write to delta patch
		if (!find) {
			/* different piece, write it! */
			// write signature of current chunk
			ret = write(f, &sc, sizeof sc);
			if (-1 == ret) {
				SETERR;
				break;
			}
			// if write chunk len is differnt from requested len
			if (ret != sizeof sc) {
				syncSetErr(ENOEXEC, "Error:: delta file signature is broken!\n");
				ret = -1;
				break;
			}
			// write current chunk ...
			ret = write(f, buf, sc.sc_len);
			if (-1 == ret) {
				SETERR;
				break;
			}
			// if write chunk len is differnt from requested len
			if (ret != sc.sc_len) {
				syncSetErr(ENOEXEC, "Error:: delta file data is broken!\n");
				ret = -1;
				break;
			}
			blk += sc.sc_len;

			continue;
		}
		// match 1st block after difference and copy signature from B
		memcpy(&sc, find, sizeof sc);
		sc.sc_magic = SIGSYNC_MAGIC;
		sc.sc_len = blk;

		// write signature from chunk B
		blk = write(f, &sc, sizeof sc);
		if (-1 == blk) {
			SETERR;
			break;
		}
		// if write chunk len is differnt from requested len
		if (blk != sizeof sc) {
			syncSetErr(ENOEXEC, "Error:: delta file end signature is broken!\n");
			ret = -1;
			break;
		}

		blk ^= blk;
	}

	// check for error or empty delta file
	if (ret == -1)
		goto end;
	fsync(f);
	if (fstat(f, &sb_f) == -1) {
		SETERR;
		ret = -1;
		goto end;
	}

	// No deferences, not needed delta.patch !!!
	if (!sb_f.st_size) {
		ret = 1;
		goto end;
	}

	/* Delta patch is READY */

	// build compressed delta file
	if (compress) {
		outf = syncOpen(csDelta, O_WRONLY);
		if (outf == -1) {
			ret = outf;
			goto end;
		}
		if (sync_Deflate(f, outf, Z_DEFAULT_COMPRESSION) == -1) {
			syncClose(outf);
			unlink(csDelta);
			ret = -1;
			goto end;
		}
		syncClose(outf);
	}

end:
	syncClose(f);
	unlink(szTemp);

	syncClose(inf);
	free(tag_table);
	munmap(chunks, sb.st_size);
	return ret;
}

/*
 * syncPatch() Apply delta patch file to target
 * @csInput = Input target file name for patch
 * @csDelta = Input Delta patch file name
 * @csPatch = After applied patch create new alternate target file, if != NULL
 * @compress = Compress output, 0 not compressed
 * return: -1 error, 0 ok, create delta patch, 1 ok, no differences and not create patch
 */
int syncPatch(const char *csInput, const char *csDelta, const char *csPatch, int compress)
{
	int inf, outf, f, d, ret, readlen;
	char szTemp[MAXPATHLEN];
	u_char *buffer, buf[CHUNK_MAX];
	struct stat sb;
	void *delta;
	struct tagPiece *piece, *pieces = NULL;
	register int i;
	off_t off;
	sync_chunk_t sc, *suffix;

	if (compress) {
		f = syncOpen(csDelta, O_RDONLY);
		if (f == -1)
			return f;
		d = syncTemp(szTemp, MAXPATHLEN);
		if (d == -1) {
			syncClose(f);
			return d;
		}
		
		if (sync_Inflate(f, d) == -1) {
			syncClose(d);
			syncClose(f);
			unlink(szTemp);
			return -1;
		} else
			syncClose(f);
	} else {
		d = syncOpen(csDelta, O_RDONLY);
		if (d == -1)
			return d;
	}

	if (fstat(d, &sb) == -1) {
		SETERR;
		syncClose(d);
		if (compress)
			unlink(szTemp);
		return -1;
	}
	delta = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, d, 0);
	if (MAP_FAILED == delta) {
		SETERR;
		syncClose(d);
		if (compress)
			unlink(szTemp);
		return -1;
	} else {
		syncClose(d);
		if (compress)
			unlink(szTemp);
	}

	if (sync_buildPatch(delta, sb.st_size, &pieces) == -1 || !pieces) {
		syncSetErr(ENOEXEC, "Error:: patch file is broken!\n");
		munmap(delta, sb.st_size);
		return -1;
	}

	inf = syncOpen(csInput, O_RDONLY);
	if (inf == -1) {
		if (pieces)
			free(pieces);
		munmap(delta, sb.st_size);
		return inf;
	}
	outf = syncOpen(csPatch, O_WRONLY);
	if (outf == -1) {
		syncClose(inf);
		if (pieces)
			free(pieces);
		munmap(delta, sb.st_size);
		return outf;
	}

	if (fstat(inf, &sb) == -1) {
		SETERR;
		ret = -1;
		goto end;
	} else {
		if (!sb.st_size) {
			ret = -1;
			goto end;
		}
	}

	ret = readlen = 0;
	buffer = NULL;
	for (i = 0, off = 0ll, suffix = NULL, piece = pieces; piece->pfx; i++, off += readlen) {

		// printf("i=%d off=%llu sfx=%p piece=%p\n", i, off, suffix, piece);

		// if input offset is less then input file size
		if (off < sb.st_size) {
			readlen = read(inf, buf, CHUNK_MAX);
			if (readlen == -1) {
				SETERR;
				ret = -1;
				break;
			}
			// if suffix find, check for correct patch
			if (suffix) {
				if (suffix->sc_len != readlen || suffix->sc_off != off) {
					syncSetErr(ENOEXEC, "Error:: patch file is broken! (wrong suffix pos)\n");
					ret = -1;
					break;
				}
				sync_mksig(i, off, buf, readlen, &sc);
				if (sc.sc_roll != suffix->sc_roll || 
						memcmp(sc.sc_cksum, suffix->sc_cksum, MD5_DIGEST_LENGTH)) {
					syncSetErr(ENOEXEC, "Error:: patch file is broken! (wrong suffix crc)\n");
					ret = -1;
					break;
				}

				suffix = NULL;
			}

			buffer = buf;
		}

		// printf("i=%d off=%llu sfx=%p piece=%p pfx=%p pfx_off=%llu\n", i, off, suffix, piece, 
		//		piece ? piece->pfx : 0l, piece->pfx ? piece->pfx->sc_off : 0l);

		// if delta chunk match!
		if (piece->pfx && piece->pfx->sc_off == off) {
			if (!piece->buf) {
				syncSetErr(ENOEXEC, "Error:: patch file is broken! (missing data)\n");
				ret = -1;
				break;
			}

			buffer = piece->buf;
			readlen = piece->pfx->sc_len;
			suffix = piece->sfx ? piece->sfx : NULL;

			piece++;

			if (suffix && off >= sb.st_size) {
				syncSetErr(ENOEXEC, "Error:: patch file is broken! (after eof find suffix)\n");
				ret = -1;
				break;
			}
		} else
			if (off >= sb.st_size) {
			       if (piece->pfx) {
					syncSetErr(ENOEXEC, "Error:: patch file is broken! (after eof find prefix)\n");
					ret = -1;
				}

				break;
			}

		ret = write(outf, buffer, readlen);
		if (ret == -1 || ret != readlen) {
			SETERR;
			break;
		}
	}

end:
	syncClose(inf);
	syncClose(outf);
	if (pieces)
		free(pieces);
	munmap(delta, sb.st_size);
	return ret;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>