X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flibpspp%2Fzip-reader.c;h=00249e0bae41be64128720410c0079c7e819a230;hb=5d1c47b1ef2d392ce223606139d45eac749d4c80;hp=b84a411fe04710c19ff71c6798c20327ac807627;hpb=c39f02a7c1d95a2c7474a8b86c33e609a01c66e1;p=pspp diff --git a/src/libpspp/zip-reader.c b/src/libpspp/zip-reader.c index b84a411fe0..00249e0bae 100644 --- a/src/libpspp/zip-reader.c +++ b/src/libpspp/zip-reader.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 2011, 2013, 2014 Free Software Foundation, Inc. + Copyright (C) 2011, 2013, 2014, 2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,23 +16,23 @@ #include +#include "libpspp/zip-reader.h" +#include "libpspp/zip-private.h" + +#include #include #include #include #include #include -#include -#include -#include -#include - -#include +#include "libpspp/assertion.h" +#include "libpspp/cast.h" +#include "libpspp/compiler.h" +#include "libpspp/integer-format.h" +#include "libpspp/str.h" -#include "str.h" - -#include "zip-reader.h" -#include "zip-private.h" +#include "gl/xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -40,6 +40,8 @@ struct zip_member { + char *file_name; /* File name. */ + char *member_name; /* Member name. */ FILE *fp; /* The stream from which the data is read */ uint32_t offset; /* Starting offset in file. */ uint32_t comp_size; /* Length of member file data, in bytes. */ @@ -47,14 +49,13 @@ struct zip_member const struct decompressor *decompressor; size_t bytes_unread; /* Number of bytes left in the member available for reading */ - struct string *errmsgs; /* A string to hold error messages. - This string is NOT owned by this object. */ + char *error; /* Error message, if any. */ void *aux; }; struct decompressor { - bool (*init) (struct zip_member *); + char *(*init) (struct zip_member *); int (*read) (struct zip_member *, void *, size_t); void (*finish) (struct zip_member *); }; @@ -81,10 +82,10 @@ get_decompressor (uint16_t c) struct zip_reader { - char *filename; /* The name of the file from which the data is read */ + int ref_cnt; + char *file_name; /* The name of the file from which the data is read */ uint16_t n_entries; /* Number of directory entries. */ struct zip_entry *entries; /* Directory entries. */ - struct string *errs; }; struct zip_entry @@ -95,29 +96,50 @@ struct zip_entry char *name; /* Name of member file. */ }; +char * WARN_UNUSED_RESULT +zip_member_steal_error (struct zip_member *zm) +{ + char *retval = zm->error; + zm->error = NULL; + return retval; +} + void zip_member_finish (struct zip_member *zm) { if (zm) { - ds_clear (zm->errmsgs); + free (zm->file_name); + free (zm->member_name); zm->decompressor->finish (zm); fclose (zm->fp); + free (zm->error); free (zm); } } +struct zip_reader * +zip_reader_ref (const struct zip_reader *zr_) +{ + struct zip_reader *zr = CONST_CAST (struct zip_reader *, zr_); + assert (zr->ref_cnt > 0); + zr->ref_cnt++; + return zr; +} + /* Destroy the zip reader */ void -zip_reader_destroy (struct zip_reader *zr) +zip_reader_unref (struct zip_reader *zr) { - int i; if (zr == NULL) return; + assert (zr->ref_cnt > 0); + if (--zr->ref_cnt) + return; - free (zr->filename); + free (zr->file_name); - for (i = 0; i < zr->n_entries; ++i) + for (int i = 0; i < zr->n_entries; ++i) { struct zip_entry *ze = &zr->entries[i]; free (ze->name); @@ -134,71 +156,66 @@ skip_bytes (FILE *f, size_t n) fseeko (f, n, SEEK_CUR); } -static bool get_bytes (FILE *f, void *x, size_t n) WARN_UNUSED_RESULT; - - -/* Read N bytes from F, storing the result in X */ -static bool +static void get_bytes (FILE *f, void *x, size_t n) { - return (n == fread (x, 1, n, f)); + if (!fread (x, n, 1, f)) + memset (x, 0, n); } -static bool get_u32 (FILE *f, uint32_t *v) WARN_UNUSED_RESULT; - - -/* Read a 32 bit value from F */ -static bool -get_u32 (FILE *f, uint32_t *v) +static uint32_t +get_u32 (FILE *f) { uint32_t x; - if (!get_bytes (f, &x, sizeof x)) - return false; -#ifdef WORDS_BIGENDIAN - *v = bswap_32 (x); -#else - *v = x; -#endif - return true; + get_bytes (f, &x, sizeof x); + return le_to_native32 (x); } -static bool get_u16 (FILE *f, uint16_t *v) WARN_UNUSED_RESULT; - - -/* Read a 16 bit value from F */ -static bool -get_u16 (FILE *f, uint16_t *v) +static uint16_t +get_u16 (FILE *f) { uint16_t x; - if (!get_bytes (f, &x, sizeof x)) - return false; -#ifdef WORDS_BIGENDIAN - *v = bswap_16 (x); -#else - *v = x; -#endif - return true; + get_bytes (f, &x, sizeof x); + return le_to_native16 (x); } +static char * WARN_UNUSED_RESULT +get_stream_error (FILE *f, const char *file_name) +{ + if (feof (f)) + return xasprintf (_("%s: unexpected end of file"), file_name); + else if (ferror (f)) + { + /* The particular error might not be in errno anymore. Try to find out + what the error was. */ + errno = 0; + char x; + return (!fread (&x, 1, sizeof x, f) && errno + ? xasprintf (_("%s: I/O error reading Zip archive (%s)"), + file_name, strerror (errno)) + : xasprintf (_("%s: I/O error reading Zip archive"), file_name)); + } + else + return NULL; +} /* Read 32 bit integer and compare it with EXPECTED. place an error string in ERR if necessary. */ -static bool -check_magic (FILE *f, uint32_t expected, struct string *err) +static char * WARN_UNUSED_RESULT +check_magic (FILE *f, const char *file_name, uint32_t expected) { - uint32_t magic; - - if (! get_u32 (f, &magic)) return false; - - if ((expected != magic)) - { - ds_put_format (err, - _("Corrupt file at 0x%llx: Expected %"PRIx32"; got %"PRIx32), - (long long int) ftello (f) - sizeof (uint32_t), expected, magic); - - return false; - } - return true; + uint32_t magic = get_u32 (f); + char *error = get_stream_error (f, file_name); + if (error) + return error; + else if (expected != magic) + return xasprintf (_("%s: corrupt archive at 0x%llx: " + "expected %#"PRIx32" but got %#"PRIx32), + file_name, + (long long int) ftello (f) - sizeof (uint32_t), + expected, magic); + else + return NULL; } @@ -207,15 +224,13 @@ check_magic (FILE *f, uint32_t expected, struct string *err) int zip_member_read (struct zip_member *zm, void *buf, size_t bytes) { - int bytes_read = 0; - - ds_clear (zm->errmsgs); - - if ( bytes > zm->bytes_unread) + if (bytes > zm->bytes_unread) bytes = zm->bytes_unread; + if (!bytes) + return 0; - bytes_read = zm->decompressor->read (zm, buf, bytes); - if ( bytes_read < 0) + int bytes_read = zm->decompressor->read (zm, buf, bytes); + if (bytes_read < 0) return bytes_read; zm->bytes_unread -= bytes_read; @@ -223,145 +238,171 @@ zip_member_read (struct zip_member *zm, void *buf, size_t bytes) return bytes_read; } +/* Read all of ZM into memory, storing the data in *DATAP and its size in *NP. + Returns NULL if successful, otherwise an error string that the caller + must eventually free(). */ +char * WARN_UNUSED_RESULT +zip_member_read_all (struct zip_reader *zr, const char *member_name, + void **datap, size_t *np) +{ + struct zip_member *zm; + char *error = zip_member_open (zr, member_name, &zm); + if (error) + { + *datap = NULL; + *np = 0; + return error; + } + + *datap = xmalloc (zm->ucomp_size); + *np = zm->ucomp_size; + + uint8_t *data = *datap; + while (zm->bytes_unread) + if (zip_member_read (zm, data + (zm->ucomp_size - zm->bytes_unread), + zm->bytes_unread) == -1) + { + char *error = zip_member_steal_error (zm); + zip_member_finish (zm); + free (*datap); + *datap = NULL; + *np = 0; + return error; + } + + zip_member_finish (zm); + return NULL; +} /* Read a central directory header from FILE and initializes ZE with it. Returns true if successful, false otherwise. On error, appends error messages to ERRS. */ -static bool -zip_header_read_next (FILE *file, struct zip_entry *ze, struct string *errs) -{ - uint16_t v, nlen, extralen; - uint16_t gp, time, date; - uint32_t expected_crc; - - uint16_t clen, diskstart, iattr; - uint32_t eattr; - uint16_t comp_type; - - if ( ! check_magic (file, MAGIC_SOCD, errs)) - return false; - - if (! get_u16 (file, &v)) return false; - if (! get_u16 (file, &v)) return false; - if (! get_u16 (file, &gp)) return false; - if (! get_u16 (file, &comp_type)) return false; - if (! get_u16 (file, &time)) return false; - if (! get_u16 (file, &date)) return false; - if (! get_u32 (file, &expected_crc)) return false; - if (! get_u32 (file, &ze->comp_size)) return false; - if (! get_u32 (file, &ze->ucomp_size)) return false; - if (! get_u16 (file, &nlen)) return false; - if (! get_u16 (file, &extralen)) return false; - if (! get_u16 (file, &clen)) return false; - if (! get_u16 (file, &diskstart)) return false; - if (! get_u16 (file, &iattr)) return false; - if (! get_u32 (file, &eattr)) return false; - if (! get_u32 (file, &ze->offset)) return false; +static char * WARN_UNUSED_RESULT +zip_header_read_next (FILE *file, const char *file_name, + struct zip_entry *ze) +{ + char *error = check_magic (file, file_name, MAGIC_SOCD); + if (error) + return error; + + get_u16 (file); /* v */ + get_u16 (file); /* v */ + get_u16 (file); /* gp */ + get_u16 (file); /* comp_type */ + get_u16 (file); /* time */ + get_u16 (file); /* date */ + get_u32 (file); /* expected_crc */ + ze->comp_size = get_u32 (file); + ze->ucomp_size = get_u32 (file); + uint16_t nlen = get_u16 (file); + uint16_t extralen = get_u16 (file); + get_u16 (file); /* clen */ + get_u16 (file); /* diskstart */ + get_u16 (file); /* iattr */ + get_u32 (file); /* eattr */ + ze->offset = get_u32 (file); + + error = get_stream_error (file, file_name); + if (error) + return error; ze->name = xzalloc (nlen + 1); - if (! get_bytes (file, ze->name, nlen)) return false; + get_bytes (file, ze->name, nlen); + error = get_stream_error (file, file_name); + if (error) + return error; skip_bytes (file, extralen); - return true; + return NULL; } -/* Create a reader from the zip called FILENAME */ -struct zip_reader * -zip_reader_create (const char *filename, struct string *errs) +/* Create a reader from the zip called FILE_NAME */ +char * WARN_UNUSED_RESULT +zip_reader_create (const char *file_name, struct zip_reader **zrp) { - uint16_t disknum, n_members, total_members; - off_t offset = 0; - uint32_t central_dir_start, central_dir_length; - - struct zip_reader *zr = xzalloc (sizeof *zr); - zr->errs = errs; - if ( zr->errs) - ds_init_empty (zr->errs); + *zrp = NULL; - FILE *file = fopen (filename, "rb"); + FILE *file = fopen (file_name, "rb"); if (!file) - { - ds_put_cstr (zr->errs, strerror (errno)); - free (zr); - return NULL; - } + return xasprintf (_("%s: open failed (%s)"), file_name, strerror (errno)); - if ( ! check_magic (file, MAGIC_LHDR, zr->errs)) + /* Check the Zip file magic. */ + char *error = check_magic (file, file_name, MAGIC_LHDR); + if (error) { fclose (file); - free (zr); - return NULL; + return error; } - if ( ! find_eocd (file, &offset)) + /* Find end of central directory record and read it. */ + off_t offset = 0; + if (! find_eocd (file, &offset)) { - ds_put_format (zr->errs, _("Cannot find central directory")); fclose (file); - free (zr); - return NULL; + return xasprintf (_("%s: cannot find central directory"), file_name); } - - if ( 0 != fseeko (file, offset, SEEK_SET)) + if (0 != fseeko (file, offset, SEEK_SET)) { - const char *mm = strerror (errno); - ds_put_format (zr->errs, _("Failed to seek to end of central directory record: %s"), mm); + error = xasprintf (_("%s: seek failed (%s)"), + file_name, strerror (errno)); fclose (file); - free (zr); - return NULL; + return error; } - - - if ( ! check_magic (file, MAGIC_EOCD, zr->errs)) + error = check_magic (file, file_name, MAGIC_EOCD); + if (error) { fclose (file); - free (zr); - return NULL; + return error; } - - if (! get_u16 (file, &disknum) - || ! get_u16 (file, &disknum) - - || ! get_u16 (file, &n_members) - || ! get_u16 (file, &total_members) - - || ! get_u32 (file, ¢ral_dir_length) - || ! get_u32 (file, ¢ral_dir_start)) + get_u16 (file); /* disknum */ + get_u16 (file); /* disknum */ + uint16_t n_members = get_u16 (file); + get_u16 (file); /* total_members */ + get_u32 (file); /* central_dir_length */ + uint32_t central_dir_start = get_u32 (file); + error = get_stream_error (file, file_name); + if (error) { fclose (file); - free (zr); - return NULL; + return error; } - if ( 0 != fseeko (file, central_dir_start, SEEK_SET)) + /* Read central directory. */ + if (0 != fseeko (file, central_dir_start, SEEK_SET)) { - const char *mm = strerror (errno); - ds_put_format (zr->errs, _("Failed to seek to central directory: %s"), mm); + error = xasprintf (_("%s: seek failed (%s)"), + file_name, strerror (errno)); fclose (file); - free (zr); return NULL; } - zr->filename = xstrdup (filename); - + struct zip_reader *zr = XZALLOC (struct zip_reader); + zr->ref_cnt = 1; + zr->file_name = xstrdup (file_name); zr->entries = xcalloc (n_members, sizeof *zr->entries); for (int i = 0; i < n_members; i++) { - if (!zip_header_read_next (file, &zr->entries[zr->n_entries], errs)) + error = zip_header_read_next (file, file_name, + &zr->entries[zr->n_entries]); + if (error) { fclose (file); - zip_reader_destroy (zr); - return NULL; + zip_reader_unref (zr); + return error; } zr->n_entries++; } - return zr; + fclose (file); + + *zrp = zr; + return NULL; } static struct zip_entry * -zip_entry_find (struct zip_reader *zr, const char *member) +zip_entry_find (const struct zip_reader *zr, const char *member) { for (int i = 0; i < zr->n_entries; ++i) { @@ -378,74 +419,86 @@ zip_reader_get_member_name(const struct zip_reader *zr, size_t idx) return idx < zr->n_entries ? zr->entries[idx].name : NULL; } +/* Returns true if ZR contains a member named MEMBER, false otherwise. */ +bool +zip_reader_contains_member (const struct zip_reader *zr, const char *member) +{ + return zip_entry_find (zr, member) != NULL; +} + /* Return the member called MEMBER from the reader ZR */ -struct zip_member * -zip_member_open (struct zip_reader *zr, const char *member) +char * WARN_UNUSED_RESULT +zip_member_open (struct zip_reader *zr, const char *member, + struct zip_member **zmp) { + *zmp = NULL; + struct zip_entry *ze = zip_entry_find (zr, member); - if ( ze == NULL) - { - ds_put_format (zr->errs, _("%s: unknown member"), member); - return NULL; - } + if (ze == NULL) + return xasprintf (_("%s: unknown member \"%s\""), + zr->file_name, member); - FILE *fp = fopen (zr->filename, "rb"); + FILE *fp = fopen (zr->file_name, "rb"); if (!fp) - { - ds_put_cstr (zr->errs, strerror (errno)); - return NULL; - } + return xasprintf ( _("%s: open failed (%s)"), + zr->file_name, strerror (errno)); struct zip_member *zm = xmalloc (sizeof *zm); + zm->file_name = xstrdup (zr->file_name); + zm->member_name = xstrdup (member); zm->fp = fp; zm->offset = ze->offset; zm->comp_size = ze->comp_size; zm->ucomp_size = ze->ucomp_size; zm->decompressor = NULL; zm->bytes_unread = ze->ucomp_size; - zm->errmsgs = zr->errs; zm->aux = NULL; + zm->error = NULL; - if ( 0 != fseeko (zm->fp, zm->offset, SEEK_SET)) + char *error; + if (0 != fseeko (zm->fp, zm->offset, SEEK_SET)) { - ds_put_format (zr->errs, _("Failed to seek to start of member `%s': %s"), - ze->name, strerror (errno)); + error = xasprintf (_("%s: seek failed (%s)"), + ze->name, strerror (errno)); goto error; } - if ( ! check_magic (zm->fp, MAGIC_LHDR, zr->errs)) + error = check_magic (zm->fp, zr->file_name, MAGIC_LHDR); + if (error) goto error; - uint16_t v, nlen, extra_len; - uint16_t gp, comp_type, time, date; - uint32_t ucomp_size, comp_size; - uint32_t crc; - if (! get_u16 (zm->fp, &v)) goto error; - if (! get_u16 (zm->fp, &gp)) goto error; - if (! get_u16 (zm->fp, &comp_type)) goto error; + get_u16 (zm->fp); /* v */ + get_u16 (zm->fp); /* gp */ + uint16_t comp_type = get_u16 (zm->fp); zm->decompressor = get_decompressor (comp_type); - if (! zm->decompressor) goto error; - if (! get_u16 (zm->fp, &time)) goto error; - if (! get_u16 (zm->fp, &date)) goto error; - if (! get_u32 (zm->fp, &crc)) goto error; - if (! get_u32 (zm->fp, &comp_size)) goto error; - - if (! get_u32 (zm->fp, &ucomp_size)) goto error; - if (! get_u16 (zm->fp, &nlen)) goto error; - if (! get_u16 (zm->fp, &extra_len)) goto error; - - char *name = xzalloc (nlen + 1); - if (! get_bytes (zm->fp, name, nlen)) + if (!zm->decompressor) { - free (name); + error = xasprintf (_("%s: member \"%s\" has unknown compression " + "type %"PRIu16), + zr->file_name, zm->member_name, comp_type); goto error; } + get_u16 (zm->fp); /* time */ + get_u16 (zm->fp); /* date */ + get_u32 (zm->fp); /* crc */ + get_u32 (zm->fp); /* comp_size */ + get_u32 (zm->fp); /* ucomp_size */ + uint16_t nlen = get_u16 (zm->fp); + uint16_t extra_len = get_u16 (zm->fp); + error = get_stream_error (zm->fp, zr->file_name); + if (error) + goto error; + + char *name = xzalloc (nlen + 1); + get_bytes (zm->fp, name, nlen); + error = get_stream_error (zm->fp, zr->file_name); + if (error) + goto error; if (strcmp (name, ze->name) != 0) { - ds_put_format (zm->errmsgs, - _("Name mismatch in zip archive. Central directory " - "says `%s'; local file header says `%s'"), - ze->name, name); + error = xasprintf (_("%s: name mismatch between central directory (%s) " + "and local file header (%s)"), + zm->file_name, ze->name, name); free (name); goto error; } @@ -453,15 +506,19 @@ zip_member_open (struct zip_reader *zr, const char *member) skip_bytes (zm->fp, extra_len); - if (!zm->decompressor->init (zm) ) + error = zm->decompressor->init (zm); + if (error) goto error; - return zm; + *zmp = zm; + return NULL; error: fclose (zm->fp); + free (zm->file_name); + free (zm->member_name); free (zm); - return NULL; + return error; } @@ -484,7 +541,7 @@ find_eocd (FILE *fp, off_t *off) because that is the minimum length of the EndOfCentralDirectory record. */ - if ( 0 > fseeko (fp, -22, SEEK_END)) + if (0 > fseeko (fp, -22, SEEK_END)) { return false; } @@ -494,12 +551,12 @@ find_eocd (FILE *fp, off_t *off) { found = probe_magic (fp, magic, start, stop, off); /* FIXME: For extra confidence lookup the directory start record here*/ - if ( start == 0) + if (start == 0) break; stop = start + sizeof (magic); start >>= 1; } - while (!found ); + while (!found); return found; } @@ -518,9 +575,9 @@ probe_magic (FILE *fp, uint32_t magic, off_t start, off_t stop, off_t *off) unsigned char seq[4]; unsigned char byte; - if ( 0 > fseeko (fp, start, SEEK_SET)) + if (0 > fseeko (fp, start, SEEK_SET)) { - return -1; + return false; } for (i = 0; i < 4 ; ++i) @@ -533,18 +590,18 @@ probe_magic (FILE *fp, uint32_t magic, off_t start, off_t stop, off_t *off) if (1 != fread (&byte, 1, 1, fp)) break; - if ( byte == seq[state]) + if (byte == seq[state]) state++; else state = 0; - if ( state == 4) + if (state == 4) { *off = ftello (fp) - 4; return true; } start++; - if ( start >= stop) + if (start >= stop) break; } while (!feof (fp)); @@ -557,13 +614,16 @@ probe_magic (FILE *fp, uint32_t magic, off_t start, off_t stop, off_t *off) static int stored_read (struct zip_member *zm, void *buf, size_t n) { - return fread (buf, 1, n, zm->fp); + size_t bytes_read = fread (buf, 1, n, zm->fp); + if (!bytes_read && !zm->error) + zm->error = get_stream_error (zm->fp, zm->file_name); + return bytes_read; } -static bool +static char * stored_init (struct zip_member *zm UNUSED) { - return true; + return NULL; } static void @@ -604,11 +664,11 @@ inflate_finish (struct zip_member *zm) free (inf); } -static bool +static char * inflate_init (struct zip_member *zm) { int r; - struct inflator *inf = xzalloc (sizeof *inf); + struct inflator *inf = XZALLOC (struct inflator); uint16_t flg = 0 ; uint16_t cmf = 0x8; /* Always 8 for inflate */ @@ -618,7 +678,7 @@ inflate_init (struct zip_member *zm) cmf |= cinfo << 4; /* Put cinfo into the high nibble */ /* make these into a 16 bit word */ - inf->cmf_flg = (cmf << 8 ) | flg; + inf->cmf_flg = (cmf << 8) | flg; /* Set the check bits */ inf->cmf_flg += 31 - (inf->cmf_flg % 31); @@ -631,15 +691,13 @@ inflate_init (struct zip_member *zm) inf->zss.opaque = Z_NULL; r = inflateInit (&inf->zss); - if ( Z_OK != r) - { - ds_put_format (zm->errmsgs, _("Cannot initialize inflator: %s"), zError (r)); - return false; - } + if (Z_OK != r) + return xasprintf (_("%s: cannot initialize inflator (%s)"), + zm->file_name, zError (r)); zm->aux = inf; - return true; + return NULL; } static int @@ -654,7 +712,7 @@ inflate_read (struct zip_member *zm, void *buf, size_t n) int bytes_to_read; int pad = 0; - if ( inf->state == 0) + if (inf->state == 0) { inf->ucomp[1] = inf->cmf_flg ; inf->ucomp[0] = inf->cmf_flg >> 8 ; @@ -672,6 +730,11 @@ inflate_read (struct zip_member *zm, void *buf, size_t n) bytes_to_read = UCOMPSIZE; bytes_read = fread (inf->ucomp + pad, 1, bytes_to_read - pad, zm->fp); + if (!bytes_read && !zm->error) + { + zm->error = get_stream_error (zm->fp, zm->file_name); + return -1; + } inf->ucomp_bytes_read += bytes_read; @@ -682,12 +745,14 @@ inflate_read (struct zip_member *zm, void *buf, size_t n) inf->zss.next_out = buf; r = inflate (&inf->zss, Z_NO_FLUSH); - if ( Z_OK == r) + if (Z_OK == r) { return n - inf->zss.avail_out; } - ds_put_format (zm->errmsgs, _("Error inflating: %s"), zError (r)); + if (!zm->error) + zm->error = xasprintf (_("%s: error inflating \"%s\" (%s)"), + zm->file_name, zm->member_name, zError (r)); return -1; }