Back to index

php5  5.3.10
zip_close.c
Go to the documentation of this file.
00001 /*
00002   zip_close.c -- close zip archive and update changes
00003   Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
00004 
00005   This file is part of libzip, a library to manipulate ZIP archives.
00006   The authors can be contacted at <libzip@nih.at>
00007 
00008   Redistribution and use in source and binary forms, with or without
00009   modification, are permitted provided that the following conditions
00010   are met:
00011   1. Redistributions of source code must retain the above copyright
00012      notice, this list of conditions and the following disclaimer.
00013   2. Redistributions in binary form must reproduce the above copyright
00014      notice, this list of conditions and the following disclaimer in
00015      the documentation and/or other materials provided with the
00016      distribution.
00017   3. The names of the authors may not be used to endorse or promote
00018      products derived from this software without specific prior
00019      written permission.
00020 
00021   THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
00022   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00023   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00024   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
00025   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00026   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00027   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00028   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
00029   IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00030   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
00031   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00032 */
00033 
00034 
00035 
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 #include <errno.h>
00040 #include <sys/types.h>
00041 #include <sys/stat.h>
00042 
00043 #include "zipint.h"
00044 
00045 static int add_data(struct zip *, struct zip_source *, struct zip_dirent *,
00046                   FILE *);
00047 static int add_data_comp(zip_source_callback, void *, struct zip_stat *,
00048                       FILE *, struct zip_error *);
00049 static int add_data_uncomp(struct zip *, zip_source_callback, void *,
00050                         struct zip_stat *, FILE *);
00051 static void ch_set_error(struct zip_error *, zip_source_callback, void *);
00052 static int copy_data(FILE *, off_t, FILE *, struct zip_error *);
00053 static int write_cdir(struct zip *, struct zip_cdir *, FILE *);
00054 static int _zip_cdir_set_comment(struct zip_cdir *, struct zip *);
00055 static int _zip_changed(struct zip *, int *);
00056 static char *_zip_create_temp_output(struct zip *, FILE **);
00057 static int _zip_torrentzip_cmp(const void *, const void *);
00058 
00059 
00060 
00061 struct filelist {
00062     int idx;
00063     const char *name;
00064 };
00065 
00066 
00067 
00068 ZIP_EXTERN(int)
00069 zip_close(struct zip *za)
00070 {
00071     int survivors;
00072     int i, j, error;
00073     char *temp;
00074     FILE *out;
00075     mode_t mask;
00076     struct zip_cdir *cd;
00077     struct zip_dirent de;
00078     struct filelist *filelist;
00079     int reopen_on_error;
00080     int new_torrentzip;
00081 
00082     reopen_on_error = 0;
00083 
00084     if (za == NULL)
00085        return -1;
00086 
00087     if (!_zip_changed(za, &survivors)) {
00088        _zip_free(za);
00089        return 0;
00090     }
00091 
00092     /* don't create zip files with no entries */
00093     if (survivors == 0) {
00094        if (za->zn && za->zp) {
00095            if (remove(za->zn) != 0) {
00096               _zip_error_set(&za->error, ZIP_ER_REMOVE, errno);
00097               return -1;
00098            }
00099        }
00100        _zip_free(za);
00101        return 0;
00102     }
00103 
00104     if ((filelist=(struct filelist *)malloc(sizeof(filelist[0])*survivors))
00105        == NULL)
00106        return -1;
00107 
00108     if ((cd=_zip_cdir_new(survivors, &za->error)) == NULL) {
00109        free(filelist);
00110        return -1;
00111     }
00112 
00113     for (i=0; i<survivors; i++)
00114        _zip_dirent_init(&cd->entry[i]);
00115 
00116     /* archive comment is special for torrentzip */
00117     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) {
00118        cd->comment = _zip_memdup(TORRENT_SIG "XXXXXXXX",
00119                               TORRENT_SIG_LEN + TORRENT_CRC_LEN,
00120                               &za->error);
00121        if (cd->comment == NULL) {
00122            _zip_cdir_free(cd);
00123            free(filelist);
00124            return -1;
00125        }
00126        cd->comment_len = TORRENT_SIG_LEN + TORRENT_CRC_LEN;
00127     }
00128     else if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, ZIP_FL_UNCHANGED) == 0) {
00129     if (_zip_cdir_set_comment(cd, za) == -1) {
00130        _zip_cdir_free(cd);
00131            free(filelist);
00132        return -1;
00133     }
00134     }
00135 
00136     if ((temp=_zip_create_temp_output(za, &out)) == NULL) {
00137        _zip_cdir_free(cd);
00138        free(filelist);
00139        return -1;
00140     }
00141 
00142 
00143     /* create list of files with index into original archive  */
00144     for (i=j=0; i<za->nentry; i++) {
00145        if (za->entry[i].state == ZIP_ST_DELETED)
00146            continue;
00147 
00148        filelist[j].idx = i;
00149        filelist[j].name = zip_get_name(za, i, 0);
00150        j++;
00151     }
00152     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
00153        qsort(filelist, survivors, sizeof(filelist[0]),
00154              _zip_torrentzip_cmp);
00155 
00156     new_torrentzip = (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 1
00157                     && zip_get_archive_flag(za, ZIP_AFL_TORRENT,
00158                                          ZIP_FL_UNCHANGED) == 0);
00159     error = 0;
00160     for (j=0; j<survivors; j++) {
00161        i = filelist[j].idx;
00162 
00163        /* create new local directory entry */
00164        if (ZIP_ENTRY_DATA_CHANGED(za->entry+i) || new_torrentzip) {
00165            _zip_dirent_init(&de);
00166 
00167            if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
00168               _zip_dirent_torrent_normalize(&de);
00169               
00170            /* use it as central directory entry */
00171            memcpy(cd->entry+j, &de, sizeof(cd->entry[j]));
00172 
00173            /* set/update file name */
00174            if (za->entry[i].ch_filename == NULL) {
00175               if (za->entry[i].state == ZIP_ST_ADDED) {
00176                   de.filename = strdup("-");
00177                   de.filename_len = 1;
00178                   cd->entry[j].filename = "-";
00179                   cd->entry[j].filename_len = 1;
00180               }
00181               else {
00182                   de.filename = strdup(za->cdir->entry[i].filename);
00183                   de.filename_len = strlen(de.filename);
00184                   cd->entry[j].filename = za->cdir->entry[i].filename;
00185                   cd->entry[j].filename_len = de.filename_len;
00186               }
00187            }
00188        }
00189        else {
00190            /* copy existing directory entries */
00191            if (fseeko(za->zp, za->cdir->entry[i].offset, SEEK_SET) != 0) {
00192               _zip_error_set(&za->error, ZIP_ER_SEEK, errno);
00193               error = 1;
00194               break;
00195            }
00196            if (_zip_dirent_read(&de, za->zp, NULL, NULL, 1,
00197                              &za->error) != 0) {
00198               error = 1;
00199               break;
00200            }
00201            memcpy(cd->entry+j, za->cdir->entry+i, sizeof(cd->entry[j]));
00202 
00203            if (de.bitflags & ZIP_GPBF_DATA_DESCRIPTOR) {
00204               de.crc = za->cdir->entry[i].crc;
00205               de.comp_size = za->cdir->entry[i].comp_size;
00206               de.uncomp_size = za->cdir->entry[i].uncomp_size;
00207               de.bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR;
00208            cd->entry[j].bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR;
00209               }
00210        }
00211 
00212        if (za->entry[i].ch_filename) {
00213            free(de.filename);
00214            if ((de.filename=strdup(za->entry[i].ch_filename)) == NULL) {
00215               error = 1;
00216               break;
00217            }
00218            de.filename_len = strlen(de.filename);
00219            cd->entry[j].filename = za->entry[i].ch_filename;
00220            cd->entry[j].filename_len = de.filename_len;
00221        }
00222 
00223        if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0
00224            && za->entry[i].ch_comment_len != -1) {
00225            /* as the rest of cd entries, its malloc/free is done by za */
00226            cd->entry[j].comment = za->entry[i].ch_comment;
00227            cd->entry[j].comment_len = za->entry[i].ch_comment_len;
00228        }
00229 
00230        cd->entry[j].offset = ftello(out);
00231 
00232        if (ZIP_ENTRY_DATA_CHANGED(za->entry+i) || new_torrentzip) {
00233            struct zip_source *zs;
00234 
00235            zs = NULL;
00236            if (!ZIP_ENTRY_DATA_CHANGED(za->entry+i)) {
00237                      if ((zs=zip_source_zip(za, za, i, ZIP_FL_RECOMPRESS, 0, -1)) == NULL) {
00238                             error = 1;
00239                             break;
00240               }
00241            }
00242 
00243            if (add_data(za, zs ? zs : za->entry[i].source, &de, out) < 0) {
00244               error = 1;
00245               break;
00246            }
00247            cd->entry[j].last_mod = de.last_mod;
00248            cd->entry[j].comp_method = de.comp_method;
00249            cd->entry[j].comp_size = de.comp_size;
00250            cd->entry[j].uncomp_size = de.uncomp_size;
00251            cd->entry[j].crc = de.crc;
00252        }
00253        else {
00254            if (_zip_dirent_write(&de, out, 1, &za->error) < 0) {
00255               error = 1;
00256               break;
00257            }
00258            /* we just read the local dirent, file is at correct position */
00259            if (copy_data(za->zp, cd->entry[j].comp_size, out,
00260                        &za->error) < 0) {
00261               error = 1;
00262               break;
00263            }
00264        }
00265 
00266        _zip_dirent_finalize(&de);
00267     }
00268 
00269     free(filelist);
00270 
00271     if (!error) {
00272        if (write_cdir(za, cd, out) < 0)
00273            error = 1;
00274     }
00275 
00276     /* pointers in cd entries are owned by za */
00277     cd->nentry = 0;
00278     _zip_cdir_free(cd);
00279 
00280     if (error) {
00281        _zip_dirent_finalize(&de);
00282        fclose(out);
00283        remove(temp);
00284        free(temp);
00285        return -1;
00286     }
00287 
00288     if (fclose(out) != 0) {
00289        _zip_error_set(&za->error, ZIP_ER_CLOSE, errno);
00290        remove(temp);
00291        free(temp);
00292        return -1;
00293     }
00294 
00295        if (za->zp) {
00296               fclose(za->zp);
00297               za->zp = NULL;
00298               reopen_on_error = 1;
00299     }
00300     if (_zip_rename(temp, za->zn) != 0) {
00301               _zip_error_set(&za->error, ZIP_ER_RENAME, errno);
00302               remove(temp);
00303               free(temp);
00304               if (reopen_on_error) {
00305               /* ignore errors, since we're already in an error case */
00306               za->zp = fopen(za->zn, "rb");
00307               }
00308               return -1;
00309        }
00310     mask = umask(0);
00311     umask(mask);
00312     chmod(za->zn, 0666&~mask);
00313     if (za->ch_comment)
00314         free(za->ch_comment);
00315 
00316     _zip_free(za);
00317        free(temp);
00318 
00319     return 0;
00320 }
00321 
00322 
00323 
00324 static int
00325 add_data(struct zip *za, struct zip_source *zs, struct zip_dirent *de, FILE *ft)
00326 {
00327     off_t offstart, offend;
00328     zip_source_callback cb;
00329     void *ud;
00330     struct zip_stat st;
00331 
00332     cb = zs->f;
00333     ud = zs->ud;
00334 
00335     if (cb(ud, &st, sizeof(st), ZIP_SOURCE_STAT) < (ssize_t)sizeof(st)) {
00336        ch_set_error(&za->error, cb, ud);
00337        return -1;
00338     }
00339 
00340     if (cb(ud, NULL, 0, ZIP_SOURCE_OPEN) < 0) {
00341        ch_set_error(&za->error, cb, ud);
00342        return -1;
00343     }
00344 
00345     offstart = ftello(ft);
00346 
00347     if (_zip_dirent_write(de, ft, 1, &za->error) < 0)
00348        return -1;
00349 
00350     if (st.comp_method != ZIP_CM_STORE) {
00351        if (add_data_comp(cb, ud, &st, ft, &za->error) < 0)
00352            return -1;
00353     }
00354     else {
00355        if (add_data_uncomp(za, cb, ud, &st, ft) < 0)
00356            return -1;
00357     }
00358 
00359     if (cb(ud, NULL, 0, ZIP_SOURCE_CLOSE) < 0) {
00360        ch_set_error(&za->error, cb, ud);
00361        return -1;
00362     }
00363 
00364     offend = ftello(ft);
00365 
00366     if (fseeko(ft, offstart, SEEK_SET) < 0) {
00367        _zip_error_set(&za->error, ZIP_ER_SEEK, errno);
00368        return -1;
00369     }
00370 
00371     
00372     de->last_mod = st.mtime;
00373     de->comp_method = st.comp_method;
00374     de->crc = st.crc;
00375     de->uncomp_size = st.size;
00376     de->comp_size = st.comp_size;
00377 
00378     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
00379        _zip_dirent_torrent_normalize(de);
00380 
00381     if (_zip_dirent_write(de, ft, 1, &za->error) < 0)
00382        return -1;
00383 
00384     if (fseeko(ft, offend, SEEK_SET) < 0) {
00385        _zip_error_set(&za->error, ZIP_ER_SEEK, errno);
00386        return -1;
00387     }
00388 
00389     return 0;
00390 }
00391 
00392 
00393 
00394 static int
00395 add_data_comp(zip_source_callback cb, void *ud, struct zip_stat *st,FILE *ft,
00396              struct zip_error *error)
00397 {
00398     char buf[BUFSIZE];
00399     ssize_t n;
00400 
00401     st->comp_size = 0;
00402     while ((n=cb(ud, buf, sizeof(buf), ZIP_SOURCE_READ)) > 0) {
00403        if (fwrite(buf, 1, n, ft) != (size_t)n) {
00404            _zip_error_set(error, ZIP_ER_WRITE, errno);
00405            return -1;
00406        }
00407 
00408        st->comp_size += n;
00409     }
00410     if (n < 0) {
00411        ch_set_error(error, cb, ud);
00412        return -1;
00413     }
00414 
00415     return 0;
00416 }
00417 
00418 
00419 
00420 static int
00421 add_data_uncomp(struct zip *za, zip_source_callback cb, void *ud,
00422               struct zip_stat *st, FILE *ft)
00423 {
00424     char b1[BUFSIZE], b2[BUFSIZE];
00425     int end, flush, ret;
00426     ssize_t n;
00427     size_t n2;
00428     z_stream zstr;
00429     int mem_level;
00430 
00431     st->comp_method = ZIP_CM_DEFLATE;
00432     st->comp_size = st->size = 0;
00433     st->crc = crc32(0, NULL, 0);
00434 
00435     zstr.zalloc = Z_NULL;
00436     zstr.zfree = Z_NULL;
00437     zstr.opaque = NULL;
00438     zstr.avail_in = 0;
00439     zstr.avail_out = 0;
00440 
00441     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
00442        mem_level = TORRENT_MEM_LEVEL;
00443     else
00444        mem_level = MAX_MEM_LEVEL;
00445 
00446     /* -MAX_WBITS: undocumented feature of zlib to _not_ write a zlib header */
00447     deflateInit2(&zstr, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, mem_level,
00448                Z_DEFAULT_STRATEGY);
00449 
00450     zstr.next_out = (Bytef *)b2;
00451     zstr.avail_out = sizeof(b2);
00452     zstr.next_in = NULL;
00453     zstr.avail_in = 0;
00454 
00455     flush = 0;
00456     end = 0;
00457     while (!end) {
00458        if (zstr.avail_in == 0 && !flush) {
00459            if ((n=cb(ud, b1, sizeof(b1), ZIP_SOURCE_READ)) < 0) {
00460               ch_set_error(&za->error, cb, ud);
00461               deflateEnd(&zstr);
00462               return -1;
00463            }
00464            if (n > 0) {
00465               zstr.avail_in = n;
00466               zstr.next_in = (Bytef *)b1;
00467               st->size += n;
00468               st->crc = crc32(st->crc, (Bytef *)b1, n);
00469            }
00470            else
00471               flush = Z_FINISH;
00472        }
00473 
00474        ret = deflate(&zstr, flush);
00475        if (ret != Z_OK && ret != Z_STREAM_END) {
00476            _zip_error_set(&za->error, ZIP_ER_ZLIB, ret);
00477            return -1;
00478        }
00479 
00480        if (zstr.avail_out != sizeof(b2)) {
00481            n2 = sizeof(b2) - zstr.avail_out;
00482 
00483            if (fwrite(b2, 1, n2, ft) != n2) {
00484               _zip_error_set(&za->error, ZIP_ER_WRITE, errno);
00485               return -1;
00486            }
00487 
00488            zstr.next_out = (Bytef *)b2;
00489            zstr.avail_out = sizeof(b2);
00490            st->comp_size += n2;
00491        }
00492 
00493        if (ret == Z_STREAM_END) {
00494            deflateEnd(&zstr);
00495            end = 1;
00496        }
00497     }
00498 
00499     return 0;
00500 }
00501 
00502 
00503 
00504 static void
00505 ch_set_error(struct zip_error *error, zip_source_callback cb, void *ud)
00506 {
00507     int e[2];
00508 
00509     if ((cb(ud, e, sizeof(e), ZIP_SOURCE_ERROR)) < (ssize_t)sizeof(e)) {
00510        error->zip_err = ZIP_ER_INTERNAL;
00511        error->sys_err = 0;
00512     }
00513     else {
00514        error->zip_err = e[0];
00515        error->sys_err = e[1];
00516     }
00517 }
00518 
00519 
00520 
00521 static int
00522 copy_data(FILE *fs, off_t len, FILE *ft, struct zip_error *error)
00523 {
00524     char buf[BUFSIZE];
00525     int n, nn;
00526 
00527     if (len == 0)
00528        return 0;
00529 
00530     while (len > 0) {
00531        nn = len > sizeof(buf) ? sizeof(buf) : len;
00532        if ((n=fread(buf, 1, nn, fs)) < 0) {
00533            _zip_error_set(error, ZIP_ER_READ, errno);
00534            return -1;
00535        }
00536        else if (n == 0) {
00537            _zip_error_set(error, ZIP_ER_EOF, 0);
00538            return -1;
00539        }
00540 
00541        if (fwrite(buf, 1, n, ft) != (size_t)n) {
00542            _zip_error_set(error, ZIP_ER_WRITE, errno);
00543            return -1;
00544        }
00545 
00546        len -= n;
00547     }
00548 
00549     return 0;
00550 }
00551 
00552 
00553 
00554 static int
00555 write_cdir(struct zip *za, struct zip_cdir *cd, FILE *out)
00556 {
00557     off_t offset;
00558     uLong crc;
00559     char buf[TORRENT_CRC_LEN+1];
00560     
00561     if (_zip_cdir_write(cd, out, &za->error) < 0)
00562        return -1;
00563     
00564     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0)
00565        return 0;
00566 
00567 
00568     /* fix up torrentzip comment */
00569 
00570     offset = ftello(out);
00571 
00572     if (_zip_filerange_crc(out, cd->offset, cd->size, &crc, &za->error) < 0)
00573        return -1;
00574 
00575     snprintf(buf, sizeof(buf), "%08lX", (long)crc);
00576 
00577     if (fseeko(out, offset-TORRENT_CRC_LEN, SEEK_SET) < 0) {
00578        _zip_error_set(&za->error, ZIP_ER_SEEK, errno);
00579        return -1;
00580     }
00581 
00582     if (fwrite(buf, TORRENT_CRC_LEN, 1, out) != 1) {
00583        _zip_error_set(&za->error, ZIP_ER_WRITE, errno);
00584        return -1;
00585     }
00586 
00587     return 0;
00588 }
00589 
00590 
00591 
00592 static int
00593 _zip_cdir_set_comment(struct zip_cdir *dest, struct zip *src)
00594 {
00595     if (src->ch_comment_len != -1) {
00596        dest->comment = _zip_memdup(src->ch_comment,
00597                                 src->ch_comment_len, &src->error);
00598        if (dest->comment == NULL)
00599            return -1;
00600        dest->comment_len = src->ch_comment_len;
00601     } else {
00602        if (src->cdir && src->cdir->comment) {
00603            dest->comment = _zip_memdup(src->cdir->comment,
00604                                    src->cdir->comment_len, &src->error);
00605            if (dest->comment == NULL)
00606               return -1;
00607            dest->comment_len = src->cdir->comment_len;
00608        }
00609     }
00610 
00611     return 0;
00612 }
00613 
00614 
00615 
00616 static int
00617 _zip_changed(struct zip *za, int *survivorsp)
00618 {
00619     int changed, i, survivors;
00620 
00621     changed = survivors = 0;
00622 
00623     if (za->ch_comment_len != -1
00624        || za->ch_flags != za->flags)
00625        changed = 1;
00626 
00627     for (i=0; i<za->nentry; i++) {
00628        if ((za->entry[i].state != ZIP_ST_UNCHANGED)
00629            || (za->entry[i].ch_comment_len != -1))
00630            changed = 1;
00631        if (za->entry[i].state != ZIP_ST_DELETED)
00632            survivors++;
00633     }
00634 
00635     *survivorsp = survivors;
00636 
00637     return changed;
00638 }
00639 
00640 
00641 
00642 static char *
00643 _zip_create_temp_output(struct zip *za, FILE **outp)
00644 {
00645     char *temp;
00646     int tfd;
00647     FILE *tfp;
00648        int len = strlen(za->zn) + 8;
00649 
00650     if ((temp=(char *)malloc(len)) == NULL) {
00651        _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
00652        return NULL;
00653     }
00654 
00655               snprintf(temp, len, "%s.XXXXXX", za->zn);
00656 
00657     if ((tfd=mkstemp(temp)) == -1) {
00658        _zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno);
00659        free(temp);
00660        return NULL;
00661     }
00662 
00663     if ((tfp=fdopen(tfd, "r+b")) == NULL) {
00664        _zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno);
00665        close(tfd);
00666        remove(temp);
00667        free(temp);
00668        return NULL;
00669     }
00670 #ifdef PHP_WIN32
00671        _setmode(_fileno(tfp), _O_BINARY );
00672 #endif
00673 
00674     *outp = tfp;
00675     return temp;
00676 }
00677 
00678 
00679 
00680 static int
00681 _zip_torrentzip_cmp(const void *a, const void *b)
00682 {
00683     return strcasecmp(((const struct filelist *)a)->name,
00684                     ((const struct filelist *)b)->name);
00685 }