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