1228753Smm/*-
2228753Smm * Copyright (c) 2008 Anselm Strauss
3228753Smm * Copyright (c) 2009 Joerg Sonnenberger
4228753Smm * All rights reserved.
5228753Smm *
6228753Smm * Redistribution and use in source and binary forms, with or without
7228753Smm * modification, are permitted provided that the following conditions
8228753Smm * are met:
9228753Smm * 1. Redistributions of source code must retain the above copyright
10228753Smm *    notice, this list of conditions and the following disclaimer.
11228753Smm * 2. Redistributions in binary form must reproduce the above copyright
12228753Smm *    notice, this list of conditions and the following disclaimer in the
13228753Smm *    documentation and/or other materials provided with the distribution.
14228753Smm *
15228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25228753Smm */
26228753Smm
27228753Smm/*
28228753Smm * Development supported by Google Summer of Code 2008.
29228753Smm */
30228753Smm
31228753Smm/*
32228753Smm * The current implementation is very limited:
33228753Smm *
34228753Smm *   - No encryption support.
35228753Smm *   - No ZIP64 support.
36228753Smm *   - No support for splitting and spanning.
37228753Smm *   - Only supports regular file and folder entries.
38228753Smm *
39228753Smm * Note that generally data in ZIP files is little-endian encoded,
40228753Smm * with some exceptions.
41228753Smm *
42228753Smm * TODO: Since Libarchive is generally 64bit oriented, but this implementation
43228753Smm * does not yet support sizes exceeding 32bit, it is highly fragile for
44228753Smm * big archives. This should change when ZIP64 is finally implemented, otherwise
45228753Smm * some serious checking has to be done.
46228753Smm *
47228753Smm */
48228753Smm
49228753Smm#include "archive_platform.h"
50229592Smm__FBSDID("$FreeBSD$");
51228753Smm
52228753Smm#ifdef HAVE_ERRNO_H
53228753Smm#include <errno.h>
54228753Smm#endif
55228753Smm#include <stdio.h>
56228753Smm#ifdef HAVE_STDLIB_H
57228753Smm#include <stdlib.h>
58228753Smm#endif
59228753Smm#ifdef HAVE_STRING_H
60228753Smm#include <string.h>
61228753Smm#endif
62228753Smm#ifdef HAVE_ZLIB_H
63228753Smm#include <zlib.h>
64228753Smm#endif
65228753Smm
66228753Smm#include "archive.h"
67228753Smm#include "archive_endian.h"
68228753Smm#include "archive_entry.h"
69228753Smm#include "archive_private.h"
70228753Smm#include "archive_write_private.h"
71228753Smm
72228753Smm#ifndef HAVE_ZLIB_H
73228753Smm#include "archive_crc32.h"
74228753Smm#endif
75228753Smm
76228753Smm#define ZIP_SIGNATURE_LOCAL_FILE_HEADER 0x04034b50
77228753Smm#define ZIP_SIGNATURE_DATA_DESCRIPTOR 0x08074b50
78228753Smm#define ZIP_SIGNATURE_FILE_HEADER 0x02014b50
79228753Smm#define ZIP_SIGNATURE_CENTRAL_DIRECTORY_END 0x06054b50
80228753Smm#define ZIP_SIGNATURE_EXTRA_TIMESTAMP 0x5455
81228753Smm#define ZIP_SIGNATURE_EXTRA_UNIX 0x7855
82228753Smm#define ZIP_VERSION_EXTRACT 0x0014 /* ZIP version 2.0 is needed. */
83228753Smm#define ZIP_VERSION_BY 0x0314 /* Made by UNIX, using ZIP version 2.0. */
84228753Smm#define ZIP_FLAGS 0x08 /* Flagging bit 3 (count from 0) for using data descriptor. */
85228753Smm
86228753Smmenum compression {
87228753Smm	COMPRESSION_STORE = 0
88228753Smm#ifdef HAVE_ZLIB_H
89228753Smm	,
90228753Smm	COMPRESSION_DEFLATE = 8
91228753Smm#endif
92228753Smm};
93228753Smm
94228753Smmstatic ssize_t archive_write_zip_data(struct archive_write *, const void *buff, size_t s);
95228753Smmstatic int archive_write_zip_finish(struct archive_write *);
96228753Smmstatic int archive_write_zip_destroy(struct archive_write *);
97228753Smmstatic int archive_write_zip_finish_entry(struct archive_write *);
98228753Smmstatic int archive_write_zip_header(struct archive_write *, struct archive_entry *);
99228753Smmstatic unsigned int dos_time(const time_t);
100228753Smmstatic size_t path_length(struct archive_entry *);
101228753Smmstatic int write_path(struct archive_entry *, struct archive_write *);
102228753Smm
103228753Smmstruct zip_local_file_header {
104228753Smm	char signature[4];
105228753Smm	char version[2];
106228753Smm	char flags[2];
107228753Smm	char compression[2];
108228753Smm	char timedate[4];
109228753Smm	char crc32[4];
110228753Smm	char compressed_size[4];
111228753Smm	char uncompressed_size[4];
112228753Smm	char filename_length[2];
113228753Smm	char extra_length[2];
114228753Smm};
115228753Smm
116228753Smmstruct zip_file_header {
117228753Smm	char signature[4];
118228753Smm	char version_by[2];
119228753Smm	char version_extract[2];
120228753Smm	char flags[2];
121228753Smm	char compression[2];
122228753Smm	char timedate[4];
123228753Smm	char crc32[4];
124228753Smm	char compressed_size[4];
125228753Smm	char uncompressed_size[4];
126228753Smm	char filename_length[2];
127228753Smm	char extra_length[2];
128228753Smm	char comment_length[2];
129228753Smm	char disk_number[2];
130228753Smm	char attributes_internal[2];
131228753Smm	char attributes_external[4];
132228753Smm	char offset[4];
133228753Smm};
134228753Smm
135228753Smmstruct zip_data_descriptor {
136228753Smm	char signature[4]; /* Not mandatory, but recommended by specification. */
137228753Smm	char crc32[4];
138228753Smm	char compressed_size[4];
139228753Smm	char uncompressed_size[4];
140228753Smm};
141228753Smm
142228753Smmstruct zip_extra_data_local {
143228753Smm	char time_id[2];
144228753Smm	char time_size[2];
145228753Smm	char time_flag[1];
146228753Smm	char mtime[4];
147228753Smm	char atime[4];
148228753Smm	char ctime[4];
149228753Smm	char unix_id[2];
150228753Smm	char unix_size[2];
151228753Smm	char unix_uid[2];
152228753Smm	char unix_gid[2];
153228753Smm};
154228753Smm
155228753Smmstruct zip_extra_data_central {
156228753Smm	char time_id[2];
157228753Smm	char time_size[2];
158228753Smm	char time_flag[1];
159228753Smm	char mtime[4];
160228753Smm	char unix_id[2];
161228753Smm	char unix_size[2];
162228753Smm};
163228753Smm
164228753Smmstruct zip_file_header_link {
165228753Smm	struct zip_file_header_link *next;
166228753Smm	struct archive_entry *entry;
167228753Smm	off_t offset;
168228753Smm	unsigned long crc32;
169228753Smm	off_t compressed_size;
170228753Smm	enum compression compression;
171228753Smm};
172228753Smm
173228753Smmstruct zip {
174228753Smm	struct zip_data_descriptor data_descriptor;
175228753Smm	struct zip_file_header_link *central_directory;
176228753Smm	struct zip_file_header_link *central_directory_end;
177228753Smm	int64_t offset;
178228753Smm	int64_t written_bytes;
179228753Smm	int64_t remaining_data_bytes;
180228753Smm	enum compression compression;
181228753Smm
182228753Smm#ifdef HAVE_ZLIB_H
183228753Smm	z_stream stream;
184228753Smm	size_t len_buf;
185228753Smm	unsigned char *buf;
186228753Smm#endif
187228753Smm};
188228753Smm
189228753Smmstruct zip_central_directory_end {
190228753Smm	char signature[4];
191228753Smm	char disk[2];
192228753Smm	char start_disk[2];
193228753Smm	char entries_disk[2];
194228753Smm	char entries[2];
195228753Smm	char size[4];
196228753Smm	char offset[4];
197228753Smm	char comment_length[2];
198228753Smm};
199228753Smm
200228753Smmstatic int
201228753Smmarchive_write_zip_options(struct archive_write *a, const char *key,
202228753Smm    const char *value)
203228753Smm{
204228753Smm	struct zip *zip = a->format_data;
205228753Smm
206228753Smm	if (strcmp(key, "compression") == 0) {
207228753Smm		if (strcmp(value, "deflate") == 0) {
208228753Smm#ifdef HAVE_ZLIB_H
209228753Smm			zip->compression = COMPRESSION_DEFLATE;
210228753Smm#else
211228753Smm			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
212228753Smm			    "deflate compression not supported");
213228753Smm			return ARCHIVE_WARN;
214228753Smm#endif
215228753Smm		} else if (strcmp(value, "store") == 0)
216228753Smm			zip->compression = COMPRESSION_STORE;
217228753Smm		else
218228753Smm			return (ARCHIVE_WARN);
219228753Smm		return (ARCHIVE_OK);
220228753Smm	}
221228753Smm	return (ARCHIVE_WARN);
222228753Smm}
223228753Smm
224228753Smmint
225228753Smmarchive_write_set_format_zip(struct archive *_a)
226228753Smm{
227228753Smm	struct archive_write *a = (struct archive_write *)_a;
228228753Smm	struct zip *zip;
229228753Smm
230228753Smm	/* If another format was already registered, unregister it. */
231228753Smm	if (a->format_destroy != NULL)
232228753Smm		(a->format_destroy)(a);
233228753Smm
234228753Smm	zip = (struct zip *) calloc(1, sizeof(*zip));
235228753Smm	if (zip == NULL) {
236228753Smm		archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data");
237228753Smm		return (ARCHIVE_FATAL);
238228753Smm	}
239228753Smm	zip->central_directory = NULL;
240228753Smm	zip->central_directory_end = NULL;
241228753Smm	zip->offset = 0;
242228753Smm	zip->written_bytes = 0;
243228753Smm	zip->remaining_data_bytes = 0;
244228753Smm
245228753Smm#ifdef HAVE_ZLIB_H
246228753Smm	zip->compression = COMPRESSION_DEFLATE;
247228753Smm	zip->len_buf = 65536;
248228753Smm	zip->buf = malloc(zip->len_buf);
249228753Smm	if (zip->buf == NULL) {
250228753Smm		archive_set_error(&a->archive, ENOMEM, "Can't allocate compression buffer");
251228753Smm		return (ARCHIVE_FATAL);
252228753Smm	}
253228753Smm#else
254228753Smm	zip->compression = COMPRESSION_STORE;
255228753Smm#endif
256228753Smm
257228753Smm	a->format_data = zip;
258228753Smm
259228753Smm	a->pad_uncompressed = 0; /* Actually not needed for now, since no compression support yet. */
260228753Smm	a->format_name = "zip";
261228753Smm	a->format_options = archive_write_zip_options;
262228753Smm	a->format_write_header = archive_write_zip_header;
263228753Smm	a->format_write_data = archive_write_zip_data;
264228753Smm	a->format_finish_entry = archive_write_zip_finish_entry;
265228753Smm	a->format_finish = archive_write_zip_finish;
266228753Smm	a->format_destroy = archive_write_zip_destroy;
267228753Smm	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
268228753Smm	a->archive.archive_format_name = "ZIP";
269228753Smm
270228753Smm	archive_le32enc(&zip->data_descriptor.signature,
271228753Smm	    ZIP_SIGNATURE_DATA_DESCRIPTOR);
272228753Smm
273228753Smm	return (ARCHIVE_OK);
274228753Smm}
275228753Smm
276228753Smmstatic int
277228753Smmarchive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
278228753Smm{
279228753Smm	struct zip *zip;
280228753Smm	struct zip_local_file_header h;
281228753Smm	struct zip_extra_data_local e;
282228753Smm	struct zip_data_descriptor *d;
283228753Smm	struct zip_file_header_link *l;
284228753Smm	int ret;
285228753Smm	int64_t size;
286228753Smm	mode_t type;
287228753Smm
288228753Smm	/* Entries other than a regular file or a folder are skipped. */
289228753Smm	type = archive_entry_filetype(entry);
290228753Smm	if ((type != AE_IFREG) & (type != AE_IFDIR)) {
291228753Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Filetype not supported");
292228753Smm		return ARCHIVE_FAILED;
293228753Smm	};
294228753Smm
295228753Smm	/* Directory entries should have a size of 0. */
296228753Smm	if (type == AE_IFDIR)
297228753Smm		archive_entry_set_size(entry, 0);
298228753Smm
299228753Smm	zip = a->format_data;
300228753Smm	d = &zip->data_descriptor;
301228753Smm	size = archive_entry_size(entry);
302228753Smm	zip->remaining_data_bytes = size;
303228753Smm
304228753Smm	/* Append archive entry to the central directory data. */
305228753Smm	l = (struct zip_file_header_link *) malloc(sizeof(*l));
306228753Smm	if (l == NULL) {
307228753Smm		archive_set_error(&a->archive, ENOMEM, "Can't allocate zip header data");
308228753Smm		return (ARCHIVE_FATAL);
309228753Smm	}
310228753Smm	l->entry = archive_entry_clone(entry);
311228753Smm	/* Initialize the CRC variable and potentially the local crc32(). */
312228753Smm	l->crc32 = crc32(0, NULL, 0);
313228753Smm	l->compression = zip->compression;
314228753Smm	l->compressed_size = 0;
315228753Smm	l->next = NULL;
316228753Smm	if (zip->central_directory == NULL) {
317228753Smm		zip->central_directory = l;
318228753Smm	} else {
319228753Smm		zip->central_directory_end->next = l;
320228753Smm	}
321228753Smm	zip->central_directory_end = l;
322228753Smm
323228753Smm	/* Store the offset of this header for later use in central directory. */
324228753Smm	l->offset = zip->written_bytes;
325228753Smm
326228753Smm	memset(&h, 0, sizeof(h));
327228753Smm	archive_le32enc(&h.signature, ZIP_SIGNATURE_LOCAL_FILE_HEADER);
328228753Smm	archive_le16enc(&h.version, ZIP_VERSION_EXTRACT);
329228753Smm	archive_le16enc(&h.flags, ZIP_FLAGS);
330228753Smm	archive_le16enc(&h.compression, zip->compression);
331228753Smm	archive_le32enc(&h.timedate, dos_time(archive_entry_mtime(entry)));
332228753Smm	archive_le16enc(&h.filename_length, (uint16_t)path_length(entry));
333228753Smm
334228753Smm	switch (zip->compression) {
335228753Smm	case COMPRESSION_STORE:
336228753Smm		/* Setting compressed and uncompressed sizes even when specification says
337228753Smm		 * to set to zero when using data descriptors. Otherwise the end of the
338228753Smm		 * data for an entry is rather difficult to find. */
339228753Smm		archive_le32enc(&h.compressed_size, size);
340228753Smm		archive_le32enc(&h.uncompressed_size, size);
341228753Smm		break;
342228753Smm#ifdef HAVE_ZLIB_H
343228753Smm	case COMPRESSION_DEFLATE:
344228753Smm		archive_le32enc(&h.uncompressed_size, size);
345228753Smm
346228753Smm		zip->stream.zalloc = Z_NULL;
347228753Smm		zip->stream.zfree = Z_NULL;
348228753Smm		zip->stream.opaque = Z_NULL;
349228753Smm		zip->stream.next_out = zip->buf;
350228753Smm		zip->stream.avail_out = zip->len_buf;
351228753Smm		if (deflateInit2(&zip->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
352228753Smm		    -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
353228753Smm			archive_set_error(&a->archive, ENOMEM, "Can't init deflate compressor");
354228753Smm			return (ARCHIVE_FATAL);
355228753Smm		}
356228753Smm		break;
357228753Smm#endif
358228753Smm	}
359228753Smm
360228753Smm	/* Formatting extra data. */
361228753Smm	archive_le16enc(&h.extra_length, sizeof(e));
362228753Smm	archive_le16enc(&e.time_id, ZIP_SIGNATURE_EXTRA_TIMESTAMP);
363228753Smm	archive_le16enc(&e.time_size, sizeof(e.time_flag) +
364228753Smm	    sizeof(e.mtime) + sizeof(e.atime) + sizeof(e.ctime));
365228753Smm	e.time_flag[0] = 0x07;
366228753Smm	archive_le32enc(&e.mtime, archive_entry_mtime(entry));
367228753Smm	archive_le32enc(&e.atime, archive_entry_atime(entry));
368228753Smm	archive_le32enc(&e.ctime, archive_entry_ctime(entry));
369228753Smm
370228753Smm	archive_le16enc(&e.unix_id, ZIP_SIGNATURE_EXTRA_UNIX);
371228753Smm	archive_le16enc(&e.unix_size, sizeof(e.unix_uid) + sizeof(e.unix_gid));
372228753Smm	archive_le16enc(&e.unix_uid, archive_entry_uid(entry));
373228753Smm	archive_le16enc(&e.unix_gid, archive_entry_gid(entry));
374228753Smm
375228753Smm	archive_le32enc(&d->uncompressed_size, size);
376228753Smm
377228753Smm	ret = (a->compressor.write)(a, &h, sizeof(h));
378228753Smm	if (ret != ARCHIVE_OK)
379228753Smm		return (ARCHIVE_FATAL);
380228753Smm	zip->written_bytes += sizeof(h);
381228753Smm
382228753Smm	ret = write_path(entry, a);
383228753Smm	if (ret <= ARCHIVE_OK)
384228753Smm		return (ARCHIVE_FATAL);
385228753Smm	zip->written_bytes += ret;
386228753Smm
387228753Smm	ret = (a->compressor.write)(a, &e, sizeof(e));
388228753Smm	if (ret != ARCHIVE_OK)
389228753Smm		return (ARCHIVE_FATAL);
390228753Smm	zip->written_bytes += sizeof(e);
391228753Smm
392228753Smm	return (ARCHIVE_OK);
393228753Smm}
394228753Smm
395228753Smmstatic ssize_t
396228753Smmarchive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
397228753Smm{
398228753Smm	int ret;
399228753Smm	struct zip *zip = a->format_data;
400228753Smm	struct zip_file_header_link *l = zip->central_directory_end;
401228753Smm
402228753Smm	if ((int64_t)s > zip->remaining_data_bytes)
403228753Smm		s = (size_t)zip->remaining_data_bytes;
404228753Smm
405228753Smm	if (s == 0) return 0;
406228753Smm
407228753Smm	switch (zip->compression) {
408228753Smm	case COMPRESSION_STORE:
409228753Smm		ret = (a->compressor.write)(a, buff, s);
410228753Smm		if (ret != ARCHIVE_OK) return (ret);
411228753Smm		zip->written_bytes += s;
412228753Smm		zip->remaining_data_bytes -= s;
413228753Smm		l->compressed_size += s;
414228753Smm		l->crc32 = crc32(l->crc32, buff, s);
415228753Smm		return (s);
416228753Smm#if HAVE_ZLIB_H
417228753Smm	case COMPRESSION_DEFLATE:
418228753Smm		zip->stream.next_in = (unsigned char*)(uintptr_t)buff;
419228753Smm		zip->stream.avail_in = s;
420228753Smm		do {
421228753Smm			ret = deflate(&zip->stream, Z_NO_FLUSH);
422228753Smm			if (ret == Z_STREAM_ERROR)
423228753Smm				return (ARCHIVE_FATAL);
424228753Smm			if (zip->stream.avail_out == 0) {
425228753Smm				ret = (a->compressor.write)(a, zip->buf, zip->len_buf);
426228753Smm				if (ret != ARCHIVE_OK)
427228753Smm					return (ret);
428228753Smm				l->compressed_size += zip->len_buf;
429228753Smm				zip->written_bytes += zip->len_buf;
430228753Smm				zip->stream.next_out = zip->buf;
431228753Smm				zip->stream.avail_out = zip->len_buf;
432228753Smm			}
433228753Smm		} while (zip->stream.avail_in != 0);
434228753Smm		zip->remaining_data_bytes -= s;
435228753Smm		/* If we have it, use zlib's fast crc32() */
436228753Smm		l->crc32 = crc32(l->crc32, buff, s);
437228753Smm		return (s);
438228753Smm#endif
439228753Smm
440228753Smm	default:
441228753Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
442228753Smm		    "Invalid ZIP compression type");
443228753Smm		return ARCHIVE_FATAL;
444228753Smm	}
445228753Smm}
446228753Smm
447228753Smmstatic int
448228753Smmarchive_write_zip_finish_entry(struct archive_write *a)
449228753Smm{
450228753Smm	/* Write the data descripter after file data has been written. */
451228753Smm	int ret;
452228753Smm	struct zip *zip = a->format_data;
453228753Smm	struct zip_data_descriptor *d = &zip->data_descriptor;
454228753Smm	struct zip_file_header_link *l = zip->central_directory_end;
455228753Smm#if HAVE_ZLIB_H
456228753Smm	size_t reminder;
457228753Smm#endif
458228753Smm
459228753Smm	switch(zip->compression) {
460228753Smm	case COMPRESSION_STORE:
461228753Smm		break;
462228753Smm#if HAVE_ZLIB_H
463228753Smm	case COMPRESSION_DEFLATE:
464228753Smm		for (;;) {
465228753Smm			ret = deflate(&zip->stream, Z_FINISH);
466228753Smm			if (ret == Z_STREAM_ERROR)
467228753Smm				return (ARCHIVE_FATAL);
468228753Smm			reminder = zip->len_buf - zip->stream.avail_out;
469228753Smm			ret = (a->compressor.write)(a, zip->buf, reminder);
470228753Smm			if (ret != ARCHIVE_OK)
471228753Smm				return (ret);
472228753Smm			l->compressed_size += reminder;
473228753Smm			zip->written_bytes += reminder;
474228753Smm			zip->stream.next_out = zip->buf;
475228753Smm			if (zip->stream.avail_out != 0)
476228753Smm				break;
477228753Smm			zip->stream.avail_out = zip->len_buf;
478228753Smm		}
479228753Smm		deflateEnd(&zip->stream);
480228753Smm		break;
481228753Smm#endif
482228753Smm	}
483228753Smm
484228753Smm	archive_le32enc(&d->crc32, l->crc32);
485228753Smm	archive_le32enc(&d->compressed_size, l->compressed_size);
486228753Smm	ret = (a->compressor.write)(a, d, sizeof(*d));
487228753Smm	if (ret != ARCHIVE_OK)
488228753Smm		return (ARCHIVE_FATAL);
489228753Smm	zip->written_bytes += sizeof(*d);
490228753Smm	return (ARCHIVE_OK);
491228753Smm}
492228753Smm
493228753Smmstatic int
494228753Smmarchive_write_zip_finish(struct archive_write *a)
495228753Smm{
496228753Smm	struct zip *zip;
497228753Smm	struct zip_file_header_link *l;
498228753Smm	struct zip_file_header h;
499228753Smm	struct zip_central_directory_end end;
500228753Smm	struct zip_extra_data_central e;
501228753Smm	off_t offset_start, offset_end;
502228753Smm	int entries;
503228753Smm	int ret;
504228753Smm
505228753Smm	if (a->compressor.write == NULL)
506228753Smm		return (ARCHIVE_OK);
507228753Smm
508228753Smm	zip = a->format_data;
509228753Smm	l = zip->central_directory;
510228753Smm
511228753Smm	/*
512228753Smm	 * Formatting central directory file header fields that are fixed for all entries.
513228753Smm	 * Fields not used (and therefor 0) are:
514228753Smm	 *
515228753Smm	 *   - comment_length
516228753Smm	 *   - disk_number
517228753Smm	 *   - attributes_internal
518228753Smm	 */
519228753Smm	memset(&h, 0, sizeof(h));
520228753Smm	archive_le32enc(&h.signature, ZIP_SIGNATURE_FILE_HEADER);
521228753Smm	archive_le16enc(&h.version_by, ZIP_VERSION_BY);
522228753Smm	archive_le16enc(&h.version_extract, ZIP_VERSION_EXTRACT);
523228753Smm	archive_le16enc(&h.flags, ZIP_FLAGS);
524228753Smm
525228753Smm	entries = 0;
526228753Smm	offset_start = zip->written_bytes;
527228753Smm
528228753Smm	/* Formatting individual header fields per entry and
529228753Smm	 * writing each entry. */
530228753Smm	while (l != NULL) {
531228753Smm		archive_le16enc(&h.compression, l->compression);
532228753Smm		archive_le32enc(&h.timedate, dos_time(archive_entry_mtime(l->entry)));
533228753Smm		archive_le32enc(&h.crc32, l->crc32);
534228753Smm		archive_le32enc(&h.compressed_size, l->compressed_size);
535228753Smm		archive_le32enc(&h.uncompressed_size, archive_entry_size(l->entry));
536228753Smm		archive_le16enc(&h.filename_length, (uint16_t)path_length(l->entry));
537228753Smm		archive_le16enc(&h.extra_length, sizeof(e));
538228753Smm		archive_le16enc(&h.attributes_external[2], archive_entry_mode(l->entry));
539228753Smm		archive_le32enc(&h.offset, l->offset);
540228753Smm
541228753Smm		/* Formatting extra data. */
542228753Smm		archive_le16enc(&e.time_id, ZIP_SIGNATURE_EXTRA_TIMESTAMP);
543228753Smm		archive_le16enc(&e.time_size, sizeof(e.mtime) + sizeof(e.time_flag));
544228753Smm		e.time_flag[0] = 0x07;
545228753Smm		archive_le32enc(&e.mtime, archive_entry_mtime(l->entry));
546228753Smm		archive_le16enc(&e.unix_id, ZIP_SIGNATURE_EXTRA_UNIX);
547228753Smm		archive_le16enc(&e.unix_size, 0x0000);
548228753Smm
549228753Smm		ret = (a->compressor.write)(a, &h, sizeof(h));
550228753Smm		if (ret != ARCHIVE_OK)
551228753Smm			return (ARCHIVE_FATAL);
552228753Smm		zip->written_bytes += sizeof(h);
553228753Smm
554228753Smm		ret = write_path(l->entry, a);
555228753Smm		if (ret <= ARCHIVE_OK)
556228753Smm			return (ARCHIVE_FATAL);
557228753Smm		zip->written_bytes += ret;
558228753Smm
559228753Smm		ret = (a->compressor.write)(a, &e, sizeof(e));
560228753Smm		if (ret != ARCHIVE_OK)
561228753Smm			return (ARCHIVE_FATAL);
562228753Smm		zip->written_bytes += sizeof(e);
563228753Smm
564228753Smm		l = l->next;
565228753Smm		entries++;
566228753Smm	}
567228753Smm	offset_end = zip->written_bytes;
568228753Smm
569228753Smm	/* Formatting end of central directory. */
570228753Smm	memset(&end, 0, sizeof(end));
571228753Smm	archive_le32enc(&end.signature, ZIP_SIGNATURE_CENTRAL_DIRECTORY_END);
572228753Smm	archive_le16enc(&end.entries_disk, entries);
573228753Smm	archive_le16enc(&end.entries, entries);
574228753Smm	archive_le32enc(&end.size, offset_end - offset_start);
575228753Smm	archive_le32enc(&end.offset, offset_start);
576228753Smm
577228753Smm	/* Writing end of central directory. */
578228753Smm	ret = (a->compressor.write)(a, &end, sizeof(end));
579228753Smm	if (ret != ARCHIVE_OK)
580228753Smm		return (ARCHIVE_FATAL);
581228753Smm	zip->written_bytes += sizeof(end);
582228753Smm	return (ARCHIVE_OK);
583228753Smm}
584228753Smm
585228753Smmstatic int
586228753Smmarchive_write_zip_destroy(struct archive_write *a)
587228753Smm{
588228753Smm	struct zip *zip;
589228753Smm	struct zip_file_header_link *l;
590228753Smm
591228753Smm	zip = a->format_data;
592228753Smm	while (zip->central_directory != NULL) {
593228753Smm	   l = zip->central_directory;
594228753Smm	   zip->central_directory = l->next;
595228753Smm	   archive_entry_free(l->entry);
596228753Smm	   free(l);
597228753Smm	}
598228753Smm#ifdef HAVE_ZLIB_H
599228753Smm	free(zip->buf);
600228753Smm#endif
601228753Smm	free(zip);
602228753Smm	a->format_data = NULL;
603228753Smm	return (ARCHIVE_OK);
604228753Smm}
605228753Smm
606228753Smm/* Convert into MSDOS-style date/time. */
607228753Smmstatic unsigned int
608228753Smmdos_time(const time_t unix_time)
609228753Smm{
610228753Smm	struct tm *t;
611228753Smm	unsigned int dt;
612228753Smm
613228753Smm	/* This will not preserve time when creating/extracting the archive
614228753Smm	 * on two systems with different time zones. */
615228753Smm	t = localtime(&unix_time);
616228753Smm
617228753Smm	dt = 0;
618228753Smm	dt += ((t->tm_year - 80) & 0x7f) << 9;
619228753Smm	dt += ((t->tm_mon + 1) & 0x0f) << 5;
620228753Smm	dt += (t->tm_mday & 0x1f);
621228753Smm	dt <<= 16;
622228753Smm	dt += (t->tm_hour & 0x1f) << 11;
623228753Smm	dt += (t->tm_min & 0x3f) << 5;
624228753Smm	dt += (t->tm_sec & 0x3e) >> 1; /* Only counting every 2 seconds. */
625228753Smm	return dt;
626228753Smm}
627228753Smm
628228753Smmstatic size_t
629228753Smmpath_length(struct archive_entry *entry)
630228753Smm{
631228753Smm	mode_t type;
632228753Smm	const char *path;
633228753Smm
634228753Smm	type = archive_entry_filetype(entry);
635228753Smm	path = archive_entry_pathname(entry);
636228753Smm
637228753Smm	if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) {
638228753Smm		return strlen(path) + 1;
639228753Smm	} else {
640228753Smm		return strlen(path);
641228753Smm	}
642228753Smm}
643228753Smm
644228753Smmstatic int
645228753Smmwrite_path(struct archive_entry *entry, struct archive_write *archive)
646228753Smm{
647228753Smm	int ret;
648228753Smm	const char *path;
649228753Smm	mode_t type;
650228753Smm	size_t written_bytes;
651228753Smm
652228753Smm	path = archive_entry_pathname(entry);
653228753Smm	type = archive_entry_filetype(entry);
654228753Smm	written_bytes = 0;
655228753Smm
656228753Smm	ret = (archive->compressor.write)(archive, path, strlen(path));
657228753Smm	if (ret != ARCHIVE_OK)
658228753Smm		return (ARCHIVE_FATAL);
659228753Smm	written_bytes += strlen(path);
660228753Smm
661228753Smm	/* Folders are recognized by a traling slash. */
662228753Smm	if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) {
663228753Smm		ret = (archive->compressor.write)(archive, "/", 1);
664228753Smm		if (ret != ARCHIVE_OK)
665228753Smm			return (ARCHIVE_FATAL);
666228753Smm		written_bytes += 1;
667228753Smm	}
668228753Smm
669228753Smm	return ((int)written_bytes);
670228753Smm}
671