/************************************************************************* * (C) 2010 AITNET ltd - Sofia/Bulgaria - * by Michael Pounov * * $Author: misho $ * $Id: aitsync.c,v 1.1 2010/03/24 16:00:15 misho Exp $ * *************************************************************************/ #include "global.h" #include "tool.h" #include "zc.h" #include "patch.h" #include "file.h" static int sync_Errno; static char sync_Error[STRSIZ]; static inline int func_comp(sync_tag_t const *t1, sync_tag_t const *t2) { return t1->st_tag - t2->st_tag; } // // Error maintenance functions ... // // sync_GetErrno() Get error code of last operation inline int sync_GetErrno() { return sync_Errno; } // sync_GetError() Get error text of last operation inline const char *sync_GetError() { return sync_Error; } // sync_SetErr() Set error to variables for internal use!!! inline void syncSetErr(int eno, char *estr, ...) { va_list lst; sync_Errno = eno; memset(sync_Error, 0, STRSIZ); va_start(lst, estr); vsnprintf(sync_Error, STRSIZ, estr, lst); va_end(lst); } // ---------------------------------------------------------- /* * syncSignature() Calculate and create signature for diff * @csInput = Input patched file name for calculating check sums * @csSig = Output Signature file name * return: -1 error, 0 ok */ int syncSignature(const char *csInput, const char *csSig) { int inf, outf, ret; u_char buf[CHUNK_MAX]; register int i = 0; off_t off = 0ll; sync_chunk_t sc; inf = syncOpen(csInput, O_RDONLY); if (inf == -1) return inf; outf = syncOpen(csSig, O_WRONLY); if (outf == -1) { syncClose(inf); return outf; } for (i = 0, off = 0ll, ret = -1; ret; i++, off += ret) { memset(buf, 0, CHUNK_MAX); ret = read(inf, buf, CHUNK_MAX); if (ret == -1) { SETERR; break; } // fill chunk sync_mksig(i, off, buf, ret, &sc); if (write(outf, &sc, sizeof sc) == -1) { SETERR; break; } } syncClose(outf); syncClose(inf); return ret; } /* * syncDelta() Create Delta patch file * @csInput = Input original source file name for make delta patch file * @csSig = Input Signature file name * @csDelta = Output Delta patch file name * @compress = Compress output, 0 not compressed * return: -1 error, 0 ok */ int syncDelta(const char *csInput, const char *csSig, const char *csDelta, int compress) { int inf, outf, f, sigf, ret, cnt; size_t blk; register int i, j, c, cx; struct stat sb, sb_f; u_long tags[TABLESIZ]; sync_tag_t *tag_table; sync_chunk_t *chunks, *find, sc; u_char buf[CHUNK_MAX]; off_t off; char szTemp[MAXPATHLEN]; /* load signatures */ sigf = syncOpen(csSig, O_RDONLY); if (sigf == -1) { return sigf; } if (fstat(sigf, &sb) == -1) { SETERR; syncClose(sigf); return -1; } else { if (!sb.st_size) { syncClose(sigf); return 1; } cnt = sb.st_size / sizeof(sync_chunk_t); if (sb.st_size % sizeof(sync_chunk_t)) { syncSetErr(ENOEXEC, "Error:: signature file is broken!\n"); syncClose(sigf); return -1; } } chunks = (sync_chunk_t*) mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, sigf, 0); if (MAP_FAILED == chunks) { SETERR; syncClose(sigf); return -1; } syncClose(sigf); /* build from signatures sorted index and hashes */ // init tags array for (i = 0; i < TABLESIZ; i++) tags[i] = NULL_TAG; // build index from signature blocks tag_table = (sync_tag_t*) calloc(cnt, sizeof(sync_tag_t)); if (!tag_table) { SETERR; munmap(chunks, sb.st_size); return -1; } else { for (i = 0; i < cnt; i++) { tag_table[i].st_id = i; tag_table[i].st_tag = GETTAG(chunks[i].sc_roll); } qsort(tag_table, cnt, sizeof(sync_tag_t), (int (*)(const void *, const void *)) func_comp); } // assign less id position in tag_table to tags for (i = cnt - 1; i > -1; i--) tags[tag_table[i].st_tag] = i; /* build delta patch */ inf = syncOpen(csInput, O_RDONLY); if (inf == -1) { free(tag_table); munmap(chunks, sb.st_size); return inf; } if (compress) f = syncTemp(szTemp, MAXPATHLEN); else f = syncOpen(csDelta, O_WRONLY); if (f == -1) { syncClose(inf); free(tag_table); munmap(chunks, sb.st_size); return f; } for (i = 0, off = 0ll, ret = -1, blk = 0; (ret = read(inf, buf, CHUNK_MAX)); i++, off += ret) { if (ret == -1) { SETERR; break; } find = NULL; // printf("+ find=%p off=%llu i=%d blk=%d\n", find, off, i, blk); // check chunk for differences with signature sync_mksig(i, off, buf, ret, &sc); cx = GETTAG(sc.sc_roll); // find in hash -> hash_sorted_table if (NULL_TAG != tags[cx] && tag_table[tags[cx]].st_tag == cx) { // find in hash_sorted_table crc == -> real chunks id for (j = 0, c = tag_table[tags[cx]].st_id; tag_table[tags[cx] + j].st_tag == cx; j++, c = tag_table[tags[cx] + j].st_id) { if (chunks[c].sc_magic == sc.sc_magic && chunks[c].sc_len == sc.sc_len && chunks[c].sc_roll == sc.sc_roll && !memcmp(chunks[c].sc_cksum, sc.sc_cksum, MD5_DIGEST_LENGTH)) { find = &chunks[c]; break; } } } // printf("+ find=%p off=%llu i=%d blk=%d\n", find, off, i, blk); // if match chunk, check for previous match if (!blk && find) continue; // if not find chunk in signature skip write to delta patch if (!find) { /* different piece, write it! */ // write signature of current chunk ret = write(f, &sc, sizeof sc); if (-1 == ret) { SETERR; break; } // if write chunk len is differnt from requested len if (ret != sizeof sc) { syncSetErr(ENOEXEC, "Error:: delta file signature is broken!\n"); ret = -1; break; } // write current chunk ... ret = write(f, buf, sc.sc_len); if (-1 == ret) { SETERR; break; } // if write chunk len is differnt from requested len if (ret != sc.sc_len) { syncSetErr(ENOEXEC, "Error:: delta file data is broken!\n"); ret = -1; break; } blk += sc.sc_len; continue; } // match 1st block after difference and copy signature from B memcpy(&sc, find, sizeof sc); sc.sc_magic = SIGSYNC_MAGIC; sc.sc_len = blk; // write signature from chunk B blk = write(f, &sc, sizeof sc); if (-1 == blk) { SETERR; break; } // if write chunk len is differnt from requested len if (blk != sizeof sc) { syncSetErr(ENOEXEC, "Error:: delta file end signature is broken!\n"); ret = -1; break; } blk ^= blk; } // check for error or empty delta file if (ret == -1) goto end; fsync(f); if (fstat(f, &sb_f) == -1) { SETERR; ret = -1; goto end; } // No deferences, not needed delta.patch !!! if (!sb_f.st_size) { ret = 1; goto end; } /* Delta patch is READY */ // build compressed delta file if (compress) { outf = syncOpen(csDelta, O_WRONLY); if (outf == -1) { ret = outf; goto end; } if (sync_Deflate(f, outf, Z_DEFAULT_COMPRESSION) == -1) { syncClose(outf); unlink(csDelta); ret = -1; goto end; } syncClose(outf); } end: syncClose(f); unlink(szTemp); syncClose(inf); free(tag_table); munmap(chunks, sb.st_size); return ret; } /* * syncPatch() Apply delta patch file to target * @csInput = Input target file name for patch * @csDelta = Input Delta patch file name * @csPatch = After applied patch create new alternate target file, if != NULL * @compress = Compress output, 0 not compressed * return: -1 error, 0 ok, create delta patch, 1 ok, no differences and not create patch */ int syncPatch(const char *csInput, const char *csDelta, const char *csPatch, int compress) { int inf, outf, f, d, ret, readlen; char szTemp[MAXPATHLEN]; u_char *buffer, buf[CHUNK_MAX]; struct stat sb; void *delta; struct tagPiece *piece, *pieces = NULL; register int i; off_t off; sync_chunk_t sc, *suffix; if (compress) { f = syncOpen(csDelta, O_RDONLY); if (f == -1) return f; d = syncTemp(szTemp, MAXPATHLEN); if (d == -1) { syncClose(f); return d; } if (sync_Inflate(f, d) == -1) { syncClose(d); syncClose(f); unlink(szTemp); return -1; } else syncClose(f); } else { d = syncOpen(csDelta, O_RDONLY); if (d == -1) return d; } if (fstat(d, &sb) == -1) { SETERR; syncClose(d); if (compress) unlink(szTemp); return -1; } delta = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, d, 0); if (MAP_FAILED == delta) { SETERR; syncClose(d); if (compress) unlink(szTemp); return -1; } else { syncClose(d); if (compress) unlink(szTemp); } if (sync_buildPatch(delta, sb.st_size, &pieces) == -1 || !pieces) { syncSetErr(ENOEXEC, "Error:: patch file is broken!\n"); munmap(delta, sb.st_size); return -1; } inf = syncOpen(csInput, O_RDONLY); if (inf == -1) { if (pieces) free(pieces); munmap(delta, sb.st_size); return inf; } outf = syncOpen(csPatch, O_WRONLY); if (outf == -1) { syncClose(inf); if (pieces) free(pieces); munmap(delta, sb.st_size); return outf; } if (fstat(inf, &sb) == -1) { SETERR; ret = -1; goto end; } else { if (!sb.st_size) { ret = -1; goto end; } } ret = readlen = 0; buffer = NULL; for (i = 0, off = 0ll, suffix = NULL, piece = pieces; piece->pfx; i++, off += readlen) { // printf("i=%d off=%llu sfx=%p piece=%p\n", i, off, suffix, piece); // if input offset is less then input file size if (off < sb.st_size) { readlen = read(inf, buf, CHUNK_MAX); if (readlen == -1) { SETERR; ret = -1; break; } // if suffix find, check for correct patch if (suffix) { if (suffix->sc_len != readlen || suffix->sc_off != off) { syncSetErr(ENOEXEC, "Error:: patch file is broken! (wrong suffix pos)\n"); ret = -1; break; } sync_mksig(i, off, buf, readlen, &sc); if (sc.sc_roll != suffix->sc_roll || memcmp(sc.sc_cksum, suffix->sc_cksum, MD5_DIGEST_LENGTH)) { syncSetErr(ENOEXEC, "Error:: patch file is broken! (wrong suffix crc)\n"); ret = -1; break; } suffix = NULL; } buffer = buf; } // printf("i=%d off=%llu sfx=%p piece=%p pfx=%p pfx_off=%llu\n", i, off, suffix, piece, // piece ? piece->pfx : 0l, piece->pfx ? piece->pfx->sc_off : 0l); // if delta chunk match! if (piece->pfx && piece->pfx->sc_off == off) { if (!piece->buf) { syncSetErr(ENOEXEC, "Error:: patch file is broken! (missing data)\n"); ret = -1; break; } buffer = piece->buf; readlen = piece->pfx->sc_len; suffix = piece->sfx ? piece->sfx : NULL; piece++; if (suffix && off >= sb.st_size) { syncSetErr(ENOEXEC, "Error:: patch file is broken! (after eof find suffix)\n"); ret = -1; break; } } else if (off >= sb.st_size) { if (piece->pfx) { syncSetErr(ENOEXEC, "Error:: patch file is broken! (after eof find prefix)\n"); ret = -1; } break; } ret = write(outf, buffer, readlen); if (ret == -1 || ret != readlen) { SETERR; break; } } end: syncClose(inf); syncClose(outf); if (pieces) free(pieces); munmap(delta, sb.st_size); return ret; }