[libgsf] Zip: use a DDESC if the sink is not seekable.
- From: Morten Welinder <mortenw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgsf] Zip: use a DDESC if the sink is not seekable.
- Date: Sat, 29 Nov 2014 20:19:16 +0000 (UTC)
commit e420fc52824ce5cd3d7a8bdb3eb10d848866f055
Author: Morten Welinder <terra gnome org>
Date: Sat Nov 29 15:18:42 2014 -0500
Zip: use a DDESC if the sink is not seekable.
ChangeLog | 3 ++
gsf/gsf-outfile-zip.c | 84 +++++++++++++++++++++++++++++++++++-------------
2 files changed, 64 insertions(+), 23 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 881b67e..7b3e08a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
2014-11-29 Morten Welinder <terra gnome org>
+ * gsf/gsf-outfile-zip.c (zip_header_write): Determine if the sink
+ is seekable. Use a DDESC, if not.
+
* gsf/gsf-output-gio.c (gsf_output_gio_write): Simplify and handle
the zero bytes cases better.
(gsf_output_gio_new_full): Check seekability here and thus only
diff --git a/gsf/gsf-outfile-zip.c b/gsf/gsf-outfile-zip.c
index dccbac9..8c92aa5 100644
--- a/gsf/gsf-outfile-zip.c
+++ b/gsf/gsf-outfile-zip.c
@@ -50,6 +50,7 @@ struct _GsfOutfileZip {
GsfOutput *sink;
GsfOutfileZip *root;
+ gint8 sink_is_seekable;
gint8 zip64;
char *entry_name;
@@ -402,13 +403,6 @@ zip_time_make (GDateTime *modtime)
return ztime;
}
-static void
-zip_dirent_update_flags (GsfZipDirent *dirent)
-{
- /* Since we can seek, do not use a ddesc. */
- dirent->flags &= ~ZIP_DIRENT_FLAGS_HAS_DDESC;
-}
-
static GsfZipDirent*
zip_dirent_new_out (GsfOutfileZip *zip)
{
@@ -435,7 +429,6 @@ zip_dirent_new_out (GsfOutfileZip *zip)
dirent->mtime = (time_t)mtime64;
dirent->zip64 = zip->zip64;
- zip_dirent_update_flags (dirent);
g_date_time_unref (modtime);
@@ -448,22 +441,63 @@ static gboolean
zip_header_write (GsfOutfileZip *zip)
{
guint8 hbuf[ZIP_HEADER_SIZE];
- GsfZipDirent const *dirent = zip->vdir->dirent;
+ GsfZipDirent *dirent = zip->vdir->dirent;
char *name = dirent->name;
int nlen = strlen (name);
gboolean ret;
- gboolean has_ddesc = (dirent->flags & ZIP_DIRENT_FLAGS_HAS_DDESC) != 0;
- guint32 crc32 = has_ddesc ? 0 : dirent->crc32;
- gsf_off_t csize = has_ddesc ? 0 : dirent->csize;
- gsf_off_t usize = has_ddesc ? 0 : dirent->usize;
- GString *extras = g_string_sized_new (ZIP_HEADER_SIZE + nlen + 100);
- gboolean real_zip64 =
+ gboolean real_zip64, has_ddesc;
+ guint32 crc32;
+ gsf_off_t csize, usize;
+ GString *extras;
+ guint8 extract = 23;
+ gboolean sig_written = FALSE;
+
+ memset (hbuf, 0, sizeof hbuf);
+ GSF_LE_SET_GUINT32 (hbuf, ZIP_HEADER_SIGNATURE);
+
+ if (zip->sink_is_seekable == -1) {
+ /*
+ * We need to figure out if the sink is seekable, but we lack
+ * an API to check that. Instead, write the signature and
+ * try to seek back onto it. If seeking back fails, just
+ * don't rewrite it.
+ */
+ if (!gsf_output_write (zip->sink, 4, hbuf))
+ return FALSE;
+ zip->sink_is_seekable =
+ gsf_output_seek (zip->sink, dirent->offset, G_SEEK_SET);
+ if (!zip->sink_is_seekable)
+ sig_written = TRUE;
+ }
+
+ /* Now figure out if we need a DDESC record. */
+ if (zip->sink_is_seekable)
+ dirent->flags &= ~ZIP_DIRENT_FLAGS_HAS_DDESC;
+ else
+ dirent->flags |= ZIP_DIRENT_FLAGS_HAS_DDESC;
+ has_ddesc = (dirent->flags & ZIP_DIRENT_FLAGS_HAS_DDESC) != 0;
+ crc32 = has_ddesc ? 0 : dirent->crc32;
+ csize = has_ddesc ? 0 : dirent->csize;
+ usize = has_ddesc ? 0 : dirent->usize;
+
+ /*
+ * Determine if we need a real zip64 extra field. We do so, if
+ * - forced
+ * - in auto mode, if usize, csize, or offset has overflowed
+ * - in auto mode, if we use a DDESC
+ */
+ real_zip64 =
(dirent->zip64 == TRUE ||
(dirent->zip64 == -1 &&
- (dirent->usize >= G_MAXUINT32 ||
+ (has_ddesc ||
+ dirent->usize >= G_MAXUINT32 ||
dirent->csize >= G_MAXUINT32 ||
dirent->offset >= G_MAXUINT32)));
- const guint8 extract = real_zip64 ? 45 : 23;
+
+ if (real_zip64)
+ extract = 45;
+
+ extras = g_string_sized_new (ZIP_HEADER_SIZE + nlen + 100);
/*
* In the has_ddesc case, we write crc32/size/usize as zero and store
@@ -473,9 +507,11 @@ zip_header_write (GsfOutfileZip *zip)
* header a second time correcting crc32/size/usize, see
* see zip_header_patch_sizes. For this reason, we must ensure that
* the record's length does not depend on the the sizes.
+ *
+ * In the the has_ddesc case we store zeroes here. No idea what we
+ * were supposed to write.
*/
-
- if (dirent->zip64) {
+ if (dirent->zip64 /* auto or forced */) {
char tmp[8];
guint16 typ = real_zip64
? ZIP_DIRENT_EXTRA_FIELD_ZIP64
@@ -502,8 +538,6 @@ zip_header_write (GsfOutfileZip *zip)
g_string_append_len (extras, tmp, 4);
}
- memset (hbuf, 0, sizeof hbuf);
- GSF_LE_SET_GUINT32 (hbuf, ZIP_HEADER_SIGNATURE);
GSF_LE_SET_GUINT16 (hbuf + ZIP_HEADER_EXTRACT,
(ZIP_OS_MSDOS << 8) + extract);
GSF_LE_SET_GUINT16 (hbuf + ZIP_HEADER_FLAGS, dirent->flags);
@@ -512,15 +546,17 @@ zip_header_write (GsfOutfileZip *zip)
GSF_LE_SET_GUINT32 (hbuf + ZIP_HEADER_DOSTIME, dirent->dostime);
GSF_LE_SET_GUINT32 (hbuf + ZIP_HEADER_CRC32, crc32);
GSF_LE_SET_GUINT32 (hbuf + ZIP_HEADER_CSIZE,
- real_zip64 ? G_MAXUINT32 : csize);
+ real_zip64 && !has_ddesc ? G_MAXUINT32 : csize);
GSF_LE_SET_GUINT32 (hbuf + ZIP_HEADER_USIZE,
- real_zip64 ? G_MAXUINT32 : usize);
+ real_zip64 && !has_ddesc ? G_MAXUINT32 : usize);
GSF_LE_SET_GUINT16 (hbuf + ZIP_HEADER_NAME_SIZE, nlen);
GSF_LE_SET_GUINT16 (hbuf + ZIP_HEADER_EXTRAS_SIZE, extras->len);
/* Stuff everything into extras so we can do just one write. */
g_string_insert_len (extras, 0, hbuf, sizeof hbuf);
g_string_insert_len (extras, sizeof hbuf, name, nlen);
+ if (sig_written)
+ g_string_erase (extras, 0, 4);
ret = gsf_output_write (zip->sink, extras->len, extras->str);
@@ -773,6 +809,7 @@ gsf_outfile_zip_set_sink (GsfOutfileZip *zip, GsfOutput *sink)
if (zip->sink)
g_object_unref (zip->sink);
zip->sink = sink;
+ zip->sink_is_seekable = -1;
}
static GsfOutput *
@@ -832,6 +869,7 @@ gsf_outfile_zip_init (GObject *obj)
GsfOutfileZip *zip = GSF_OUTFILE_ZIP (obj);
zip->sink = NULL;
+ zip->sink_is_seekable = -1;
zip->root = NULL;
zip->entry_name = NULL;
zip->zip64 = -1;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]