Annotation of libaitsync/src/aitsync.c, revision 1.3.2.1
1.1 misho 1: /*************************************************************************
2: * (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
3: * by Michael Pounov <misho@openbsd-bg.org>
4: *
5: * $Author: misho $
1.3.2.1 ! misho 6: * $Id: aitsync.c,v 1.3 2012/07/22 22:09:47 misho Exp $
1.1 misho 7: *
1.2 misho 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:
1.3 misho 15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
1.2 misho 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: */
1.1 misho 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:
1.3 misho 57: static inline int
58: func_comp(sync_tag_t const *t1, sync_tag_t const *t2)
1.1 misho 59: {
60: return t1->st_tag - t2->st_tag;
61: }
62:
1.3.2.1 ! misho 63: /*
! 64: * Error maintenance functions ...
! 65: */
1.1 misho 66:
67: // sync_GetErrno() Get error code of last operation
1.3 misho 68: inline int
69: sync_GetErrno()
1.1 misho 70: {
71: return sync_Errno;
72: }
73:
74: // sync_GetError() Get error text of last operation
1.3 misho 75: inline const char *
76: sync_GetError()
1.1 misho 77: {
78: return sync_Error;
79: }
80:
81: // sync_SetErr() Set error to variables for internal use!!!
1.3 misho 82: inline void
83: syncSetErr(int eno, char *estr, ...)
1.1 misho 84: {
85: va_list lst;
86:
87: sync_Errno = eno;
88: memset(sync_Error, 0, STRSIZ);
89: va_start(lst, estr);
90: vsnprintf(sync_Error, STRSIZ, estr, lst);
91: va_end(lst);
92: }
93:
1.3.2.1 ! misho 94: /* ---------------------------------------------------------- */
1.1 misho 95:
96: /*
1.3.2.1 ! misho 97: * syncSignature() - Calculate and create signature for diff
! 98: *
! 99: * @csInput = Input file name for calculating check sums
1.1 misho 100: * @csSig = Output Signature file name
1.2 misho 101: * @compress = 2 compress signatures output, 0 not compressed
1.1 misho 102: * return: -1 error, 0 ok
103: */
1.3 misho 104: int
105: syncSignature(const char *csInput, const char *csSig, int compress)
1.1 misho 106: {
1.2 misho 107: int inf, outf, f, ret;
1.1 misho 108: u_char buf[CHUNK_MAX];
109: register int i = 0;
110: off_t off = 0ll;
111: sync_chunk_t sc;
1.2 misho 112: char szTemp[MAXPATHLEN];
1.1 misho 113:
1.3.2.1 ! misho 114: inf = syncOpen(csInput, O_RDONLY, 0);
1.1 misho 115: if (inf == -1)
116: return inf;
1.2 misho 117: if (compress & 2)
118: f = syncTemp(szTemp, MAXPATHLEN);
119: else
1.3.2.1 ! misho 120: f = syncOpen(csSig, O_WRONLY, 0);
1.2 misho 121: if (f == -1) {
1.1 misho 122: syncClose(inf);
1.2 misho 123: return f;
1.1 misho 124: }
125:
126: for (i = 0, off = 0ll, ret = -1; ret; i++, off += ret) {
127: memset(buf, 0, CHUNK_MAX);
128: ret = read(inf, buf, CHUNK_MAX);
129: if (ret == -1) {
130: SETERR;
131: break;
132: }
133:
134: // fill chunk
135: sync_mksig(i, off, buf, ret, &sc);
136:
1.2 misho 137: if (write(f, &sc, sizeof sc) == -1) {
1.1 misho 138: SETERR;
139: break;
140: }
141: }
142:
1.2 misho 143: /* Signatures is READY */
144:
145: // build compressed delta file
146: if (compress & 2) {
1.3.2.1 ! misho 147: outf = syncOpen(csSig, O_WRONLY, 0);
1.2 misho 148: if (outf == -1) {
149: ret = outf;
150: goto end;
151: }
152: if (sync_Deflate(f, outf, Z_DEFAULT_COMPRESSION) == -1) {
153: syncClose(outf);
154: unlink(csSig);
155: ret = -1;
156: goto end;
157: }
158: syncClose(outf);
159: }
160: end:
161: syncClose(f);
162: if (compress & 2)
163: unlink(szTemp);
164:
1.1 misho 165: syncClose(inf);
166: return ret;
167: }
168:
169: /*
170: * syncDelta() Create Delta patch file
171: * @csInput = Input original source file name for make delta patch file
172: * @csSig = Input Signature file name
173: * @csDelta = Output Delta patch file name
1.2 misho 174: * @compress = 3 everything compress, 2 compressed signatures, 1 compress delta output, 0 not compressed
1.1 misho 175: * return: -1 error, 0 ok
176: */
1.3 misho 177: int
178: syncDelta(const char *csInput, const char *csSig, const char *csDelta, int compress)
1.1 misho 179: {
180: int inf, outf, f, sigf, ret, cnt;
181: size_t blk;
182: register int i, j, c, cx;
183: struct stat sb, sb_f;
184: u_long tags[TABLESIZ];
185: sync_tag_t *tag_table;
186: sync_chunk_t *chunks, *find, sc;
187: u_char buf[CHUNK_MAX];
188: off_t off;
189: char szTemp[MAXPATHLEN];
190:
191: /* load signatures */
192:
1.2 misho 193: if (compress & 2) {
1.3.2.1 ! misho 194: f = syncOpen(csSig, O_RDONLY, 0);
1.2 misho 195: if (-1 == f)
196: return f;
197: sigf = syncTemp(szTemp, MAXPATHLEN);
198: if (-1 == sigf) {
199: syncClose(f);
200: return sigf;
201: }
202:
203: if (sync_Inflate(f, sigf) == -1) {
204: syncClose(sigf);
205: syncClose(f);
206: unlink(szTemp);
207: return -1;
208: } else
209: syncClose(f);
210: } else {
1.3.2.1 ! misho 211: sigf = syncOpen(csSig, O_RDONLY, 0);
1.2 misho 212: if (-1 == sigf)
213: return sigf;
1.1 misho 214: }
1.2 misho 215:
1.1 misho 216: if (fstat(sigf, &sb) == -1) {
217: SETERR;
218: syncClose(sigf);
1.2 misho 219: if (compress & 2)
220: unlink(szTemp);
1.1 misho 221: return -1;
222: } else {
223: if (!sb.st_size) {
224: syncClose(sigf);
1.2 misho 225: if (compress & 2)
226: unlink(szTemp);
1.1 misho 227: return 1;
228: }
229:
230: cnt = sb.st_size / sizeof(sync_chunk_t);
231: if (sb.st_size % sizeof(sync_chunk_t)) {
232: syncSetErr(ENOEXEC, "Error:: signature file is broken!\n");
233: syncClose(sigf);
1.2 misho 234: if (compress & 2)
235: unlink(szTemp);
1.1 misho 236: return -1;
237: }
238: }
239: chunks = (sync_chunk_t*) mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, sigf, 0);
240: if (MAP_FAILED == chunks) {
241: SETERR;
242: syncClose(sigf);
1.2 misho 243: if (compress & 2)
244: unlink(szTemp);
1.1 misho 245: return -1;
1.2 misho 246: } else {
247: syncClose(sigf);
248: if (compress & 2)
249: unlink(szTemp);
1.1 misho 250: }
251:
252: /* build from signatures sorted index and hashes */
253:
254: // init tags array
255: for (i = 0; i < TABLESIZ; i++)
256: tags[i] = NULL_TAG;
257:
258: // build index from signature blocks
259: tag_table = (sync_tag_t*) calloc(cnt, sizeof(sync_tag_t));
260: if (!tag_table) {
261: SETERR;
262: munmap(chunks, sb.st_size);
263: return -1;
264: } else {
265: for (i = 0; i < cnt; i++) {
266: tag_table[i].st_id = i;
267: tag_table[i].st_tag = GETTAG(chunks[i].sc_roll);
268: }
269:
270: qsort(tag_table, cnt, sizeof(sync_tag_t), (int (*)(const void *, const void *)) func_comp);
271: }
272: // assign less id position in tag_table to tags
273: for (i = cnt - 1; i > -1; i--)
274: tags[tag_table[i].st_tag] = i;
275:
276: /* build delta patch */
277:
1.3.2.1 ! misho 278: inf = syncOpen(csInput, O_RDONLY, 0);
1.1 misho 279: if (inf == -1) {
280: free(tag_table);
281: munmap(chunks, sb.st_size);
282: return inf;
283: }
1.2 misho 284: if (compress & 1)
1.1 misho 285: f = syncTemp(szTemp, MAXPATHLEN);
286: else
1.3.2.1 ! misho 287: f = syncOpen(csDelta, O_WRONLY, 0);
1.1 misho 288: if (f == -1) {
289: syncClose(inf);
290: free(tag_table);
291: munmap(chunks, sb.st_size);
292: return f;
293: }
294:
295: for (i = 0, off = 0ll, ret = -1, blk = 0; (ret = read(inf, buf, CHUNK_MAX)); i++, off += ret) {
296: if (ret == -1) {
297: SETERR;
298: break;
299: }
300: find = NULL;
301:
302: // printf("+ find=%p off=%llu i=%d blk=%d\n", find, off, i, blk);
303:
304: // check chunk for differences with signature
305: sync_mksig(i, off, buf, ret, &sc);
306: cx = GETTAG(sc.sc_roll);
307: // find in hash -> hash_sorted_table
308: if (NULL_TAG != tags[cx] && tag_table[tags[cx]].st_tag == cx) {
309: // find in hash_sorted_table crc == -> real chunks id
310: for (j = 0, c = tag_table[tags[cx]].st_id; tag_table[tags[cx] + j].st_tag == cx;
311: j++, c = tag_table[tags[cx] + j].st_id) {
312: if (chunks[c].sc_magic == sc.sc_magic && chunks[c].sc_len == sc.sc_len &&
313: chunks[c].sc_roll == sc.sc_roll &&
314: !memcmp(chunks[c].sc_cksum, sc.sc_cksum, MD5_DIGEST_LENGTH)) {
315: find = &chunks[c];
316: break;
317: }
318: }
319: }
320:
321: // printf("+ find=%p off=%llu i=%d blk=%d\n", find, off, i, blk);
322:
323: // if match chunk, check for previous match
324: if (!blk && find)
325: continue;
326: // if not find chunk in signature skip write to delta patch
327: if (!find) {
328: /* different piece, write it! */
329: // write signature of current chunk
330: ret = write(f, &sc, sizeof sc);
331: if (-1 == ret) {
332: SETERR;
333: break;
334: }
335: // if write chunk len is differnt from requested len
336: if (ret != sizeof sc) {
337: syncSetErr(ENOEXEC, "Error:: delta file signature is broken!\n");
338: ret = -1;
339: break;
340: }
341: // write current chunk ...
342: ret = write(f, buf, sc.sc_len);
343: if (-1 == ret) {
344: SETERR;
345: break;
346: }
347: // if write chunk len is differnt from requested len
348: if (ret != sc.sc_len) {
349: syncSetErr(ENOEXEC, "Error:: delta file data is broken!\n");
350: ret = -1;
351: break;
352: }
353: blk += sc.sc_len;
354:
355: continue;
356: }
357: // match 1st block after difference and copy signature from B
358: memcpy(&sc, find, sizeof sc);
359: sc.sc_magic = SIGSYNC_MAGIC;
360: sc.sc_len = blk;
361:
362: // write signature from chunk B
363: blk = write(f, &sc, sizeof sc);
364: if (-1 == blk) {
365: SETERR;
366: break;
367: }
368: // if write chunk len is differnt from requested len
369: if (blk != sizeof sc) {
370: syncSetErr(ENOEXEC, "Error:: delta file end signature is broken!\n");
371: ret = -1;
372: break;
373: }
374:
375: blk ^= blk;
376: }
377:
378: // check for error or empty delta file
379: if (ret == -1)
380: goto end;
381: fsync(f);
382: if (fstat(f, &sb_f) == -1) {
383: SETERR;
384: ret = -1;
385: goto end;
386: }
387:
388: // No deferences, not needed delta.patch !!!
389: if (!sb_f.st_size) {
390: ret = 1;
391: goto end;
392: }
393:
394: /* Delta patch is READY */
395:
396: // build compressed delta file
1.2 misho 397: if (compress & 1) {
1.3.2.1 ! misho 398: outf = syncOpen(csDelta, O_WRONLY, 0);
1.1 misho 399: if (outf == -1) {
400: ret = outf;
401: goto end;
402: }
403: if (sync_Deflate(f, outf, Z_DEFAULT_COMPRESSION) == -1) {
404: syncClose(outf);
405: unlink(csDelta);
406: ret = -1;
407: goto end;
408: }
409: syncClose(outf);
410: }
411:
412: end:
413: syncClose(f);
1.2 misho 414: if (compress & 1)
415: unlink(szTemp);
1.1 misho 416:
417: syncClose(inf);
418: free(tag_table);
419: munmap(chunks, sb.st_size);
420: return ret;
421: }
422:
423: /*
424: * syncPatch() Apply delta patch file to target
425: * @csInput = Input target file name for patch
426: * @csDelta = Input Delta patch file name
427: * @csPatch = After applied patch create new alternate target file, if != NULL
1.2 misho 428: * @compress = 1 compress delta input, 0 not compressed
1.1 misho 429: * return: -1 error, 0 ok, create delta patch, 1 ok, no differences and not create patch
430: */
1.3 misho 431: int
432: syncPatch(const char *csInput, const char *csDelta, const char *csPatch, int compress)
1.1 misho 433: {
434: int inf, outf, f, d, ret, readlen;
435: char szTemp[MAXPATHLEN];
436: u_char *buffer, buf[CHUNK_MAX];
437: struct stat sb;
438: void *delta;
439: struct tagPiece *piece, *pieces = NULL;
440: register int i;
441: off_t off;
442: sync_chunk_t sc, *suffix;
443:
1.2 misho 444: if (compress & 1) {
1.3.2.1 ! misho 445: f = syncOpen(csDelta, O_RDONLY, 0);
1.1 misho 446: if (f == -1)
447: return f;
448: d = syncTemp(szTemp, MAXPATHLEN);
449: if (d == -1) {
450: syncClose(f);
451: return d;
452: }
453:
454: if (sync_Inflate(f, d) == -1) {
455: syncClose(d);
456: syncClose(f);
457: unlink(szTemp);
458: return -1;
459: } else
460: syncClose(f);
461: } else {
1.3.2.1 ! misho 462: d = syncOpen(csDelta, O_RDONLY, 0);
1.1 misho 463: if (d == -1)
464: return d;
465: }
466:
467: if (fstat(d, &sb) == -1) {
468: SETERR;
469: syncClose(d);
1.2 misho 470: if (compress & 1)
1.1 misho 471: unlink(szTemp);
472: return -1;
473: }
474: delta = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, d, 0);
475: if (MAP_FAILED == delta) {
476: SETERR;
477: syncClose(d);
1.2 misho 478: if (compress & 1)
1.1 misho 479: unlink(szTemp);
480: return -1;
481: } else {
482: syncClose(d);
1.2 misho 483: if (compress & 1)
1.1 misho 484: unlink(szTemp);
485: }
486:
487: if (sync_buildPatch(delta, sb.st_size, &pieces) == -1 || !pieces) {
488: syncSetErr(ENOEXEC, "Error:: patch file is broken!\n");
489: munmap(delta, sb.st_size);
490: return -1;
491: }
492:
1.3.2.1 ! misho 493: inf = syncOpen(csInput, O_RDONLY, 0);
1.1 misho 494: if (inf == -1) {
495: if (pieces)
496: free(pieces);
497: munmap(delta, sb.st_size);
498: return inf;
499: }
1.3.2.1 ! misho 500: outf = syncOpen(csPatch, O_WRONLY, 0);
1.1 misho 501: if (outf == -1) {
502: syncClose(inf);
503: if (pieces)
504: free(pieces);
505: munmap(delta, sb.st_size);
506: return outf;
507: }
508:
509: if (fstat(inf, &sb) == -1) {
510: SETERR;
511: ret = -1;
512: goto end;
513: } else {
514: if (!sb.st_size) {
515: ret = -1;
516: goto end;
517: }
518: }
519:
520: ret = readlen = 0;
521: buffer = NULL;
522: for (i = 0, off = 0ll, suffix = NULL, piece = pieces; piece->pfx; i++, off += readlen) {
523:
524: // printf("i=%d off=%llu sfx=%p piece=%p\n", i, off, suffix, piece);
525:
526: // if input offset is less then input file size
527: if (off < sb.st_size) {
528: readlen = read(inf, buf, CHUNK_MAX);
529: if (readlen == -1) {
530: SETERR;
531: ret = -1;
532: break;
533: }
534: // if suffix find, check for correct patch
535: if (suffix) {
536: if (suffix->sc_len != readlen || suffix->sc_off != off) {
537: syncSetErr(ENOEXEC, "Error:: patch file is broken! (wrong suffix pos)\n");
538: ret = -1;
539: break;
540: }
541: sync_mksig(i, off, buf, readlen, &sc);
542: if (sc.sc_roll != suffix->sc_roll ||
543: memcmp(sc.sc_cksum, suffix->sc_cksum, MD5_DIGEST_LENGTH)) {
544: syncSetErr(ENOEXEC, "Error:: patch file is broken! (wrong suffix crc)\n");
545: ret = -1;
546: break;
547: }
548:
549: suffix = NULL;
550: }
551:
552: buffer = buf;
553: }
554:
555: // printf("i=%d off=%llu sfx=%p piece=%p pfx=%p pfx_off=%llu\n", i, off, suffix, piece,
556: // piece ? piece->pfx : 0l, piece->pfx ? piece->pfx->sc_off : 0l);
557:
558: // if delta chunk match!
559: if (piece->pfx && piece->pfx->sc_off == off) {
560: if (!piece->buf) {
561: syncSetErr(ENOEXEC, "Error:: patch file is broken! (missing data)\n");
562: ret = -1;
563: break;
564: }
565:
566: buffer = piece->buf;
567: readlen = piece->pfx->sc_len;
568: suffix = piece->sfx ? piece->sfx : NULL;
569:
570: piece++;
571:
572: if (suffix && off >= sb.st_size) {
573: syncSetErr(ENOEXEC, "Error:: patch file is broken! (after eof find suffix)\n");
574: ret = -1;
575: break;
576: }
577: } else
578: if (off >= sb.st_size) {
579: if (piece->pfx) {
580: syncSetErr(ENOEXEC, "Error:: patch file is broken! (after eof find prefix)\n");
581: ret = -1;
582: }
583:
584: break;
585: }
586:
587: ret = write(outf, buffer, readlen);
588: if (ret == -1 || ret != readlen) {
589: SETERR;
590: break;
591: }
592: }
593:
594: end:
595: syncClose(inf);
596: syncClose(outf);
597: if (pieces)
598: free(pieces);
599: munmap(delta, sb.st_size);
600: return ret;
601: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>