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