Annotation of libaitsync/src/aitsync.c, revision 1.2

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.2     ! misho       6: * $Id: aitsync.c,v 1.1.1.1.2.2 2011/05/09 14:35:56 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: 
        !            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: */
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: 
                     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
1.2     ! misho      96:  * @compress = 2 compress signatures output, 0 not compressed
1.1       misho      97:  * return: -1 error, 0 ok
                     98:  */
1.2     ! misho      99: int syncSignature(const char *csInput, const char *csSig, int compress)
1.1       misho     100: {
1.2     ! misho     101:        int inf, outf, f, ret;
1.1       misho     102:        u_char buf[CHUNK_MAX];
                    103:        register int i = 0;
                    104:        off_t off = 0ll;
                    105:        sync_chunk_t sc;
1.2     ! misho     106:        char szTemp[MAXPATHLEN];
1.1       misho     107: 
                    108:        inf = syncOpen(csInput, O_RDONLY);
                    109:        if (inf == -1)
                    110:                return inf;
1.2     ! misho     111:        if (compress & 2)
        !           112:                f = syncTemp(szTemp, MAXPATHLEN);
        !           113:        else
        !           114:                f = syncOpen(csSig, O_WRONLY);
        !           115:        if (f == -1) {
1.1       misho     116:                syncClose(inf);
1.2     ! misho     117:                return f;
1.1       misho     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: 
1.2     ! misho     131:                if (write(f, &sc, sizeof sc) == -1) {
1.1       misho     132:                        SETERR;
                    133:                        break;
                    134:                }
                    135:        }
                    136: 
1.2     ! misho     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: 
1.1       misho     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
1.2     ! misho     168:  * @compress = 3 everything compress, 2 compressed signatures, 1 compress delta output, 0 not compressed
1.1       misho     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: 
1.2     ! misho     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;
1.1       misho     207:        }
1.2     ! misho     208: 
1.1       misho     209:        if (fstat(sigf, &sb) == -1) {
                    210:                SETERR;
                    211:                syncClose(sigf);
1.2     ! misho     212:                if (compress & 2)
        !           213:                        unlink(szTemp);
1.1       misho     214:                return -1;
                    215:        } else {
                    216:                if (!sb.st_size) {
                    217:                        syncClose(sigf);
1.2     ! misho     218:                        if (compress & 2)
        !           219:                                unlink(szTemp);
1.1       misho     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);
1.2     ! misho     227:                        if (compress & 2)
        !           228:                                unlink(szTemp);
1.1       misho     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);
1.2     ! misho     236:                if (compress & 2)
        !           237:                        unlink(szTemp);
1.1       misho     238:                return -1;
1.2     ! misho     239:        } else {
        !           240:                syncClose(sigf);
        !           241:                if (compress & 2)
        !           242:                        unlink(szTemp);
1.1       misho     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:        }
1.2     ! misho     277:        if (compress & 1)
1.1       misho     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
1.2     ! misho     390:        if (compress & 1) {
1.1       misho     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);
1.2     ! misho     407:        if (compress & 1)
        !           408:                unlink(szTemp);
1.1       misho     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
1.2     ! misho     421:  * @compress = 1 compress delta input, 0 not compressed
1.1       misho     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: 
1.2     ! misho     436:        if (compress & 1) {
1.1       misho     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);
1.2     ! misho     462:                if (compress & 1)
1.1       misho     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);
1.2     ! misho     470:                if (compress & 1)
1.1       misho     471:                        unlink(szTemp);
                    472:                return -1;
                    473:        } else {
                    474:                syncClose(d);
1.2     ! misho     475:                if (compress & 1)
1.1       misho     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>