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