File:  [ELWIX - Embedded LightWeight unIX -] / libaitsync / src / aitsync.c
Revision 1.6: download - view: text, annotated - select for diffs - revision graph
Tue Feb 4 16:58:17 2014 UTC (10 years, 4 months ago) by misho
Branches: MAIN
CVS tags: sync2_3, sync2_2, SYNC2_2, SYNC2_1, HEAD
version 2.1

    1: /*************************************************************************
    2: * (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
    3: *  by Michael Pounov <misho@openbsd-bg.org>
    4: *
    5: * $Author: misho $
    6: * $Id: aitsync.c,v 1.6 2014/02/04 16:58:17 misho Exp $
    7: *
    8: **************************************************************************
    9: The ELWIX and AITNET software is distributed under the following
   10: terms:
   11: 
   12: All of the documentation and software included in the ELWIX and AITNET
   13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
   14: 
   15: Copyright 2004 - 2014
   16: 	by Michael Pounov <misho@elwix.org>.  All rights reserved.
   17: 
   18: Redistribution and use in source and binary forms, with or without
   19: modification, are permitted provided that the following conditions
   20: are met:
   21: 1. Redistributions of source code must retain the above copyright
   22:    notice, this list of conditions and the following disclaimer.
   23: 2. Redistributions in binary form must reproduce the above copyright
   24:    notice, this list of conditions and the following disclaimer in the
   25:    documentation and/or other materials provided with the distribution.
   26: 3. All advertising materials mentioning features or use of this software
   27:    must display the following acknowledgement:
   28: This product includes software developed by Michael Pounov <misho@elwix.org>
   29: ELWIX - Embedded LightWeight unIX and its contributors.
   30: 4. Neither the name of AITNET nor the names of its contributors
   31:    may be used to endorse or promote products derived from this software
   32:    without specific prior written permission.
   33: 
   34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
   35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   37: ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   44: SUCH DAMAGE.
   45: */
   46: #include "global.h"
   47: #include "tool.h"
   48: #include "zc.h"
   49: #include "patch.h"
   50: #include "file.h"
   51: 
   52: 
   53: static int sync_Errno;
   54: static char sync_Error[STRSIZ];
   55: 
   56: 
   57: static inline int
   58: func_comp(sync_tag_t const *t1, sync_tag_t const *t2)
   59: {
   60: 	return t1->st_tag - t2->st_tag;
   61: }
   62: 
   63: /*
   64:  * Error maintenance functions ...
   65:  */
   66: 
   67: // sync_GetErrno() Get error code of last operation
   68: int
   69: sync_GetErrno()
   70: {
   71: 	return sync_Errno;
   72: }
   73: 
   74: // sync_GetError() Get error text of last operation
   75: const char *
   76: sync_GetError()
   77: {
   78: 	return sync_Error;
   79: }
   80: 
   81: // sync_SetErr() Set error to variables for internal use!!!
   82: void
   83: sync_SetErr(int eno, char *estr, ...)
   84: {
   85: 	va_list lst;
   86: 
   87: 	sync_Errno = eno;
   88: 	memset(sync_Error, 0, sizeof sync_Error);
   89: 	va_start(lst, estr);
   90: 	vsnprintf(sync_Error, sizeof sync_Error, estr, lst);
   91: 	va_end(lst);
   92: }
   93: 
   94: /* ---------------------------------------------------------- */
   95: 
   96: /*
   97:  * syncSignature() - Calculate and create signature for diff
   98:  *
   99:  * @csInput = Input target file name for calculating check sums
  100:  * @csSig = Output Signature file name
  101:  * @compress = 2 compress signatures output, 0 not compressed
  102:  * return: -1 error, 0 ok
  103:  */
  104: int
  105: syncSignature(const char *csInput, const char *csSig, int compress)
  106: {
  107: 	int inf, outf, f, ret;
  108: 	u_char buf[CHUNK_MAX];
  109: 	register int i = 0;
  110: 	off_t off = 0ll;
  111: 	sync_chunk_t sc;
  112: 	char szTemp[MAXPATHLEN];
  113: 
  114: 	/* open work files */
  115: 	inf = sync_Open(csInput, O_RDONLY, 0);
  116: 	if (inf == -1)
  117: 		return inf;
  118: 	if (compress & 2)
  119: 		f = sync_Temp(szTemp, sizeof szTemp);
  120: 	else
  121: 		f = sync_Open(csSig, O_WRONLY, 0);
  122: 	if (f == -1) {
  123: 		sync_Close(inf);
  124: 		return f;
  125: 	}
  126: 
  127: 	for (i = 0, off = 0ll, ret = -1; ret; i++, off += ret) {
  128: 		memset(buf, 0, CHUNK_MAX);
  129: 		ret = read(inf, buf, CHUNK_MAX);
  130: 		if (ret == -1) {
  131: 			LOGERR;
  132: 			break;
  133: 		}
  134: 
  135: 		/* fill chunk */
  136: 		sync_mksig(i, off, buf, ret, &sc);
  137: 
  138: 		if (write(f, &sc, sizeof sc) == -1) {
  139: 			LOGERR;
  140: 			break;
  141: 		}
  142: 	}
  143: 
  144: 	/* Signatures are READY */
  145: 
  146: 	if (compress & 2) {
  147: 		/* build compressed delta file */
  148: 		outf = sync_Open(csSig, O_WRONLY, 0);
  149: 		if (outf == -1) {
  150: 			ret = outf;
  151: 			goto end;
  152: 		}
  153: 		if (sync_Deflate(f, outf, Z_DEFAULT_COMPRESSION) == -1) {
  154: 			sync_Close(outf);
  155: 			unlink(csSig);
  156: 			ret = -1;
  157: 			goto end;
  158: 		}
  159: 		sync_Close(outf);
  160: 	}
  161: end:
  162: 	sync_Close(f);
  163: 	if (compress & 2)
  164: 		unlink(szTemp);
  165: 	sync_Close(inf);
  166: 	return ret;
  167: }
  168: 
  169: /*
  170:  * syncDelta() - Create Delta patch file
  171:  *
  172:  * @csInput = Input original source file name for make delta patch file
  173:  * @csSig = Input target Signature file name
  174:  * @csDelta = Output Delta patch file name
  175:  * @compress = 3 everything compress, 2 compressed signatures, 1 compress delta output, 0 not compressed
  176:  * return: -1 error, 0 ok
  177:  */
  178: int
  179: syncDelta(const char *csInput, const char *csSig, const char *csDelta, int compress)
  180: {
  181: 	int inf, outf, f, sigf, ret, cnt;
  182: 	size_t blk;
  183: 	register int i, j, c, cx;
  184: 	struct stat sb, sb_f;
  185: 	u_long tags[TABLESIZ];
  186: 	sync_tag_t *tag_table;
  187: 	sync_chunk_t *chunks, *find, sc;
  188: 	u_char buf[CHUNK_MAX];
  189: 	off_t off;
  190: 	char szTemp[MAXPATHLEN];
  191: 
  192: 	/* load signatures */
  193: 
  194: 	if (compress & 2) {
  195: 		f = sync_Open(csSig, O_RDONLY, 0);
  196: 		if (-1 == f)
  197: 			return f;
  198: 		sigf = sync_Temp(szTemp, sizeof szTemp);
  199: 		if (-1 == sigf) {
  200: 			sync_Close(f);
  201: 			return sigf;
  202: 		}
  203: 
  204: 		if (sync_Inflate(f, sigf) == -1) {
  205: 			sync_Close(sigf);
  206: 			sync_Close(f);
  207: 			unlink(szTemp);
  208: 			return -1;
  209: 		} else
  210: 			sync_Close(f);
  211: 	} else {
  212: 		sigf = sync_Open(csSig, O_RDONLY, 0);
  213: 		if (-1 == sigf)
  214: 			return sigf;
  215: 	}
  216: 
  217: 	if (fstat(sigf, &sb) == -1) {
  218: 		LOGERR;
  219: 		sync_Close(sigf);
  220: 		if (compress & 2)
  221: 			unlink(szTemp);
  222: 		return -1;
  223: 	} else {
  224: 		if (!sb.st_size) {
  225: 			sync_Close(sigf);
  226: 			if (compress & 2)
  227: 				unlink(szTemp);
  228: 			return 1;
  229: 		}
  230: 
  231: 		cnt = sb.st_size / sizeof(sync_chunk_t);
  232: 		if (sb.st_size % sizeof(sync_chunk_t)) {
  233: 			sync_SetErr(ENOEXEC, "Error:: signature file is broken!\n");
  234: 			sync_Close(sigf);
  235: 			if (compress & 2)
  236: 				unlink(szTemp);
  237: 			return -1;
  238: 		}
  239: 	}
  240: 	chunks = (sync_chunk_t*) mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, sigf, 0);
  241: 	if (MAP_FAILED == chunks) {
  242: 		LOGERR;
  243: 		sync_Close(sigf);
  244: 		if (compress & 2)
  245: 			unlink(szTemp);
  246: 		return -1;
  247: 	} else {
  248: 		sync_Close(sigf);
  249: 		if (compress & 2)
  250: 			unlink(szTemp);
  251: 	}
  252: 
  253: 	/* build from signatures sorted index and hashes */
  254: 
  255: 	/* init first stage tags array index */
  256: 	for (i = 0; i < TABLESIZ; i++)
  257: 		tags[i] = NULL_TAG;
  258: 
  259: 	/* build second index from signature blocks */
  260: 	tag_table = (sync_tag_t*) e_calloc(cnt, sizeof(sync_tag_t));
  261: 	if (!tag_table) {
  262: 		LOGERR;
  263: 		munmap(chunks, sb.st_size);
  264: 		return -1;
  265: 	} else {
  266: 		for (i = 0; i < cnt; i++) {
  267: 			tag_table[i].st_id = i;
  268: 			tag_table[i].st_tag = GETTAG(chunks[i].sc_roll);
  269: 		}
  270: 
  271: 		qsort(tag_table, cnt, sizeof(sync_tag_t), 
  272: 				(int (*)(const void *, const void *)) func_comp);
  273: 	}
  274: 	/* assign less id position in tag_table to tags. 
  275: 	 * It made relation between 1st & 2nd indexes */
  276: 	for (i = cnt - 1; i > -1; i--)
  277: 		tags[tag_table[i].st_tag] = i;
  278: 
  279: 
  280: 	/* build delta patch */
  281: 
  282: 	inf = sync_Open(csInput, O_RDONLY, 0);
  283: 	if (inf == -1) {
  284: 		e_free(tag_table);
  285: 		munmap(chunks, sb.st_size);
  286: 		return inf;
  287: 	}
  288: 	if (compress & 1)
  289: 		f = sync_Temp(szTemp, sizeof szTemp);
  290: 	else
  291: 		f = sync_Open(csDelta, O_WRONLY, 0);
  292: 	if (f == -1) {
  293: 		sync_Close(inf);
  294: 		e_free(tag_table);
  295: 		munmap(chunks, sb.st_size);
  296: 		return f;
  297: 	}
  298: 
  299: 	for (i = 0, off = 0ll, ret = -1, blk = 0; 
  300: 			(ret = read(inf, buf, CHUNK_MAX)); i++, off += ret) {
  301: 		if (ret == -1) {
  302: 			LOGERR;
  303: 			break;
  304: 		}
  305: 		find = NULL;
  306: 
  307: #if 0
  308: 		printf("+ find=%p off=%llu i=%d blk=%d\n", find, off, i, blk);
  309: #endif
  310: 
  311: 		/* check chunk for differences with signature */
  312: 		sync_mksig(i, off, buf, ret, &sc);
  313: 		cx = GETTAG(sc.sc_roll);
  314: 		/* find in hash -> hash_sorted_table */
  315: 		if (NULL_TAG != tags[cx] && tag_table[tags[cx]].st_tag == cx) {
  316: 			/* find in hash_sorted_table crc == -> real chunks id */
  317: 			for (j = 0, c = tag_table[tags[cx]].st_id; 
  318: 					tag_table[tags[cx] + j].st_tag == cx; 
  319: 					j++, c = tag_table[tags[cx] + j].st_id) {
  320: 				if (chunks[c].sc_magic == sc.sc_magic && 
  321: 						chunks[c].sc_len == sc.sc_len && 
  322: 						chunks[c].sc_roll == sc.sc_roll && 
  323: 						!memcmp(chunks[c].sc_cksum, sc.sc_cksum, MD5_DIGEST_LENGTH)) {
  324: 					find = &chunks[c];
  325: 					break;
  326: 				}
  327: 			}
  328: 		}
  329: 
  330: #if 0
  331: 		printf("+ find=%p off=%llu i=%d blk=%d\n", find, off, i, blk);
  332: #endif
  333: 
  334: 		/* if match chunk, check for previous match */
  335: 		if (!blk && find)
  336: 			continue;
  337: 		/* if not find chunk in signature skip write to delta patch */
  338: 		if (!find) {
  339: 			/* different piece, write it! 
  340: 			 * Write signature of current chunk */
  341: 			ret = write(f, &sc, sizeof sc);
  342: 			if (-1 == ret) {
  343: 				LOGERR;
  344: 				break;
  345: 			}
  346: 			/* if write chunk len is differnt from requested len */
  347: 			if (ret != sizeof sc) {
  348: 				sync_SetErr(ENOEXEC, "Error:: delta file signature is broken!\n");
  349: 				ret = -1;
  350: 				break;
  351: 			}
  352: 			/* write current chunk data ... */
  353: 			ret = write(f, buf, sc.sc_len);
  354: 			if (-1 == ret) {
  355: 				LOGERR;
  356: 				break;
  357: 			}
  358: 			/* if write chunk len is differnt from requested len */
  359: 			if (ret != sc.sc_len) {
  360: 				sync_SetErr(ENOEXEC, "Error:: delta file data is broken!\n");
  361: 				ret = -1;
  362: 				break;
  363: 			}
  364: 			blk += sc.sc_len;
  365: 
  366: 			continue;
  367: 		}
  368: 		/* match 1st block after difference and copy signature from B */
  369: 		memcpy(&sc, find, sizeof sc);
  370: 		sc.sc_magic = SIGSYNC_MAGIC;
  371: 		sc.sc_len = blk;
  372: 
  373: 		/* write signature from chunk B */
  374: 		blk = write(f, &sc, sizeof sc);
  375: 		if (-1 == blk) {
  376: 			LOGERR;
  377: 			break;
  378: 		}
  379: 		/* if write chunk len is differnt from requested len */
  380: 		if (blk != sizeof sc) {
  381: 			sync_SetErr(ENOEXEC, "Error:: delta file end signature is broken!\n");
  382: 			ret = -1;
  383: 			break;
  384: 		}
  385: 
  386: 		blk ^= blk;
  387: 	}
  388: 
  389: 	/* check for error or empty delta file */
  390: 	if (ret == -1)
  391: 		goto end;
  392: 	fsync(f);
  393: 	if (fstat(f, &sb_f) == -1) {
  394: 		LOGERR;
  395: 		ret = -1;
  396: 		goto end;
  397: 	}
  398: 
  399: 	/* No deferences, not needed delta.patch !!! */
  400: 	if (!sb_f.st_size) {
  401: 		ret = 1;
  402: 		goto end;
  403: 	}
  404: 
  405: 	/* Delta patch is READY */
  406: 
  407: 	/* build compressed delta file */
  408: 	if (compress & 1) {
  409: 		outf = sync_Open(csDelta, O_WRONLY, 0);
  410: 		if (outf == -1) {
  411: 			ret = outf;
  412: 			goto end;
  413: 		}
  414: 		if (sync_Deflate(f, outf, Z_DEFAULT_COMPRESSION) == -1) {
  415: 			sync_Close(outf);
  416: 			unlink(csDelta);
  417: 			ret = -1;
  418: 			goto end;
  419: 		}
  420: 		sync_Close(outf);
  421: 	}
  422: 
  423: end:
  424: 	sync_Close(f);
  425: 	if (compress & 1)
  426: 		unlink(szTemp);
  427: 	sync_Close(inf);
  428: 	e_free(tag_table);
  429: 	munmap(chunks, sb.st_size);
  430: 	return ret;
  431: }
  432: 
  433: /*
  434:  * syncPatch() - Apply delta patch file to target
  435:  *
  436:  * @csInput = Input target file name for patch
  437:  * @csDelta = Input Delta patch file name
  438:  * @csPatch = After applied patch create new alternate target file, if != NULL
  439:  * @compress = 1 compress delta input, 0 not compressed
  440:  * return: -1 error, 0 ok, create delta patch, 1 ok, no differences and not create patch
  441:  */
  442: int
  443: syncPatch(const char *csInput, const char *csDelta, const char *csPatch, int compress)
  444: {
  445: 	int inf, outf, f, d, ret, readlen;
  446: 	char szTemp[MAXPATHLEN];
  447: 	u_char *buffer, buf[CHUNK_MAX];
  448: 	struct stat sb;
  449: 	void *delta;
  450: 	struct tagPiece *piece, *pieces = NULL;
  451: 	register int i;
  452: 	off_t off;
  453: 	sync_chunk_t sc, *suffix;
  454: 
  455: 	if (compress & 1) {
  456: 		f = sync_Open(csDelta, O_RDONLY, 0);
  457: 		if (f == -1)
  458: 			return f;
  459: 		d = sync_Temp(szTemp, sizeof szTemp);
  460: 		if (d == -1) {
  461: 			sync_Close(f);
  462: 			return d;
  463: 		}
  464: 		
  465: 		if (sync_Inflate(f, d) == -1) {
  466: 			sync_Close(d);
  467: 			sync_Close(f);
  468: 			unlink(szTemp);
  469: 			return -1;
  470: 		} else
  471: 			sync_Close(f);
  472: 	} else {
  473: 		d = sync_Open(csDelta, O_RDONLY, 0);
  474: 		if (d == -1)
  475: 			return d;
  476: 	}
  477: 
  478: 	if (fstat(d, &sb) == -1) {
  479: 		LOGERR;
  480: 		sync_Close(d);
  481: 		if (compress & 1)
  482: 			unlink(szTemp);
  483: 		return -1;
  484: 	}
  485: 	delta = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, d, 0);
  486: 	if (MAP_FAILED == delta) {
  487: 		LOGERR;
  488: 		sync_Close(d);
  489: 		if (compress & 1)
  490: 			unlink(szTemp);
  491: 		return -1;
  492: 	} else {
  493: 		sync_Close(d);
  494: 		if (compress & 1)
  495: 			unlink(szTemp);
  496: 	}
  497: 
  498: 	if (sync_buildPatch(delta, sb.st_size, &pieces) == -1 || !pieces) {
  499: 		sync_SetErr(ENOEXEC, "Error:: patch file is broken!\n");
  500: 		munmap(delta, sb.st_size);
  501: 		return -1;
  502: 	}
  503: 
  504: 	inf = sync_Open(csInput, O_RDONLY, 0);
  505: 	if (inf == -1) {
  506: 		e_free(pieces);
  507: 		munmap(delta, sb.st_size);
  508: 		return inf;
  509: 	}
  510: 	outf = sync_Open(csPatch, O_WRONLY, 0);
  511: 	if (outf == -1) {
  512: 		sync_Close(inf);
  513: 		e_free(pieces);
  514: 		munmap(delta, sb.st_size);
  515: 		return outf;
  516: 	}
  517: 
  518: 	if (fstat(inf, &sb) == -1) {
  519: 		LOGERR;
  520: 		ret = -1;
  521: 		goto end;
  522: 	} else {
  523: 		if (!sb.st_size) {
  524: 			ret = -1;
  525: 			goto end;
  526: 		}
  527: 	}
  528: 
  529: 	ret = readlen = 0;
  530: 	buffer = NULL;
  531: 	for (i = 0, off = 0ll, suffix = NULL, piece = pieces; piece->pfx; 
  532: 			i++, off += readlen) {
  533: #if 0
  534: 		printf("i=%d off=%llu sfx=%p piece=%p\n", i, off, suffix, piece);
  535: #endif
  536: 
  537: 		/* if input offset is less then input file size */
  538: 		if (off < sb.st_size) {
  539: 			readlen = read(inf, buf, CHUNK_MAX);
  540: 			if (readlen == -1) {
  541: 				LOGERR;
  542: 				ret = -1;
  543: 				break;
  544: 			}
  545: 			/* if suffix find, check for correct patch */
  546: 			if (suffix) {
  547: 				if (suffix->sc_len != readlen || 
  548: 						suffix->sc_off != off) {
  549: 					sync_SetErr(ENOEXEC, "Error:: patch file is broken! "
  550: 							"(wrong suffix pos)\n");
  551: 					ret = -1;
  552: 					break;
  553: 				}
  554: 				sync_mksig(i, off, buf, readlen, &sc);
  555: 				if (sc.sc_roll != suffix->sc_roll || 
  556: 						memcmp(sc.sc_cksum, suffix->sc_cksum, 
  557: 							MD5_DIGEST_LENGTH)) {
  558: 					sync_SetErr(ENOEXEC, "Error:: patch file is broken! "
  559: 							"(wrong suffix crc)\n");
  560: 					ret = -1;
  561: 					break;
  562: 				}
  563: 
  564: 				suffix = NULL;
  565: 			}
  566: 
  567: 			buffer = buf;
  568: 		}
  569: 
  570: #if 0
  571: 		printf("i=%d off=%llu sfx=%p piece=%p pfx=%p pfx_off=%llu\n", i, off, 
  572: 				suffix, piece, piece ? piece->pfx : 0l, 
  573: 				piece->pfx ? piece->pfx->sc_off : 0l);
  574: #endif
  575: 
  576: 		/* if delta chunk match! */
  577: 		if (piece->pfx && piece->pfx->sc_off == off) {
  578: 			if (!piece->buf) {
  579: 				sync_SetErr(ENOEXEC, "Error:: patch file is broken! "
  580: 						"(missing data)\n");
  581: 				ret = -1;
  582: 				break;
  583: 			}
  584: 
  585: 			buffer = piece->buf;
  586: 			readlen = piece->pfx->sc_len;
  587: 			suffix = piece->sfx ? piece->sfx : NULL;
  588: 
  589: 			piece++;
  590: 
  591: 			if (suffix && off >= sb.st_size) {
  592: 				sync_SetErr(ENOEXEC, "Error:: patch file is broken! "
  593: 						"(after eof find suffix)\n");
  594: 				ret = -1;
  595: 				break;
  596: 			}
  597: 		} else if (off >= sb.st_size) {
  598: 		       if (piece->pfx) {
  599: 				sync_SetErr(ENOEXEC, "Error:: patch file is broken! "
  600: 						"(after eof find prefix)\n");
  601: 				ret = -1;
  602: 			}
  603: 
  604: 			break;
  605: 		}
  606: 
  607: 		ret = write(outf, buffer, readlen);
  608: 		if (ret == -1 || ret != readlen) {
  609: 			LOGERR;
  610: 			break;
  611: 		}
  612: 	}
  613: 
  614: end:
  615: 	sync_Close(inf);
  616: 	sync_Close(outf);
  617: 	e_free(pieces);
  618: 	munmap(delta, sb.st_size);
  619: 	return ret;
  620: }

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