1248590Smm/*-
2248590Smm * Copyright (c) 2003-2007 Tim Kientzle
3248590Smm * Copyright (c) 2011-2012 Michihiro NAKAJIMA
4248590Smm * All rights reserved.
5248590Smm *
6248590Smm * Redistribution and use in source and binary forms, with or without
7248590Smm * modification, are permitted provided that the following conditions
8248590Smm * are met:
9248590Smm * 1. Redistributions of source code must retain the above copyright
10248590Smm *    notice, this list of conditions and the following disclaimer.
11248590Smm * 2. Redistributions in binary form must reproduce the above copyright
12248590Smm *    notice, this list of conditions and the following disclaimer in the
13248590Smm *    documentation and/or other materials provided with the distribution.
14248590Smm *
15248590Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16248590Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17248590Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18248590Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19248590Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20248590Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21248590Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22248590Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23248590Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24248590Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25248590Smm */
26248590Smm
27248590Smm#include "archive_platform.h"
28248590Smm__FBSDID("$FreeBSD$");
29248590Smm
30248590Smm
31248590Smm#ifdef HAVE_ERRNO_H
32248590Smm#include <errno.h>
33248590Smm#endif
34248590Smm#include <stdio.h>
35248590Smm#ifdef HAVE_STDLIB_H
36248590Smm#include <stdlib.h>
37248590Smm#endif
38248590Smm#ifdef HAVE_STRING_H
39248590Smm#include <string.h>
40248590Smm#endif
41248590Smm
42248590Smm#include "archive.h"
43248590Smm#include "archive_entry.h"
44248590Smm#include "archive_entry_locale.h"
45248590Smm#include "archive_private.h"
46248590Smm#include "archive_write_private.h"
47358090Smm#include "archive_write_set_format_private.h"
48248590Smm
49248590Smmstruct v7tar {
50248590Smm	uint64_t	entry_bytes_remaining;
51248590Smm	uint64_t	entry_padding;
52248590Smm
53248590Smm	struct archive_string_conv *opt_sconv;
54248590Smm	struct archive_string_conv *sconv_default;
55248590Smm	int	init_default_conversion;
56248590Smm};
57248590Smm
58248590Smm/*
59248590Smm * Define structure of POSIX 'v7tar' tar header.
60248590Smm */
61248590Smm#define	V7TAR_name_offset 0
62248590Smm#define	V7TAR_name_size 100
63248590Smm#define	V7TAR_mode_offset 100
64248590Smm#define	V7TAR_mode_size 6
65248590Smm#define	V7TAR_mode_max_size 8
66248590Smm#define	V7TAR_uid_offset 108
67248590Smm#define	V7TAR_uid_size 6
68248590Smm#define	V7TAR_uid_max_size 8
69248590Smm#define	V7TAR_gid_offset 116
70248590Smm#define	V7TAR_gid_size 6
71248590Smm#define	V7TAR_gid_max_size 8
72248590Smm#define	V7TAR_size_offset 124
73248590Smm#define	V7TAR_size_size 11
74248590Smm#define	V7TAR_size_max_size 12
75248590Smm#define	V7TAR_mtime_offset 136
76248590Smm#define	V7TAR_mtime_size 11
77248590Smm#define	V7TAR_mtime_max_size 12
78248590Smm#define	V7TAR_checksum_offset 148
79248590Smm#define	V7TAR_checksum_size 8
80248590Smm#define	V7TAR_typeflag_offset 156
81248590Smm#define	V7TAR_typeflag_size 1
82248590Smm#define	V7TAR_linkname_offset 157
83248590Smm#define	V7TAR_linkname_size 100
84248590Smm#define	V7TAR_padding_offset 257
85248590Smm#define	V7TAR_padding_size 255
86248590Smm
87248590Smm/*
88248590Smm * A filled-in copy of the header for initialization.
89248590Smm */
90248590Smmstatic const char template_header[] = {
91248590Smm	/* name: 100 bytes */
92248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
93248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
94248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
95248590Smm	0,0,0,0,
96248590Smm	/* Mode, space-null termination: 8 bytes */
97248590Smm	'0','0','0','0','0','0', ' ','\0',
98248590Smm	/* uid, space-null termination: 8 bytes */
99248590Smm	'0','0','0','0','0','0', ' ','\0',
100248590Smm	/* gid, space-null termination: 8 bytes */
101248590Smm	'0','0','0','0','0','0', ' ','\0',
102311042Smm	/* size, space termination: 12 bytes */
103248590Smm	'0','0','0','0','0','0','0','0','0','0','0', ' ',
104311042Smm	/* mtime, space termination: 12 bytes */
105248590Smm	'0','0','0','0','0','0','0','0','0','0','0', ' ',
106248590Smm	/* Initial checksum value: 8 spaces */
107248590Smm	' ',' ',' ',' ',' ',' ',' ',' ',
108248590Smm	/* Typeflag: 1 byte */
109248590Smm	0,
110248590Smm	/* Linkname: 100 bytes */
111248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
112248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
113248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
114248590Smm	0,0,0,0,
115248590Smm	/* Padding: 255 bytes */
116248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
117248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
118248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
119248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
120248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
121248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
122248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
123248590Smm	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0
124248590Smm};
125248590Smm
126248590Smmstatic ssize_t	archive_write_v7tar_data(struct archive_write *a, const void *buff,
127248590Smm		    size_t s);
128248590Smmstatic int	archive_write_v7tar_free(struct archive_write *);
129248590Smmstatic int	archive_write_v7tar_close(struct archive_write *);
130248590Smmstatic int	archive_write_v7tar_finish_entry(struct archive_write *);
131248590Smmstatic int	archive_write_v7tar_header(struct archive_write *,
132248590Smm		    struct archive_entry *entry);
133248590Smmstatic int	archive_write_v7tar_options(struct archive_write *,
134248590Smm		    const char *, const char *);
135248590Smmstatic int	format_256(int64_t, char *, int);
136248590Smmstatic int	format_number(int64_t, char *, int size, int max, int strict);
137248590Smmstatic int	format_octal(int64_t, char *, int);
138248590Smmstatic int	format_header_v7tar(struct archive_write *, char h[512],
139248590Smm		    struct archive_entry *, int, struct archive_string_conv *);
140248590Smm
141248590Smm/*
142248590Smm * Set output format to 'v7tar' format.
143248590Smm */
144248590Smmint
145248590Smmarchive_write_set_format_v7tar(struct archive *_a)
146248590Smm{
147248590Smm	struct archive_write *a = (struct archive_write *)_a;
148248590Smm	struct v7tar *v7tar;
149248590Smm
150248590Smm	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
151248590Smm	    ARCHIVE_STATE_NEW, "archive_write_set_format_v7tar");
152248590Smm
153248590Smm	/* If someone else was already registered, unregister them. */
154248590Smm	if (a->format_free != NULL)
155248590Smm		(a->format_free)(a);
156248590Smm
157248590Smm	/* Basic internal sanity test. */
158248590Smm	if (sizeof(template_header) != 512) {
159248590Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
160248590Smm		    "Internal: template_header wrong size: %zu should be 512",
161248590Smm		    sizeof(template_header));
162248590Smm		return (ARCHIVE_FATAL);
163248590Smm	}
164248590Smm
165311042Smm	v7tar = (struct v7tar *)calloc(1, sizeof(*v7tar));
166248590Smm	if (v7tar == NULL) {
167248590Smm		archive_set_error(&a->archive, ENOMEM,
168248590Smm		    "Can't allocate v7tar data");
169248590Smm		return (ARCHIVE_FATAL);
170248590Smm	}
171248590Smm	a->format_data = v7tar;
172248590Smm	a->format_name = "tar (non-POSIX)";
173248590Smm	a->format_options = archive_write_v7tar_options;
174248590Smm	a->format_write_header = archive_write_v7tar_header;
175248590Smm	a->format_write_data = archive_write_v7tar_data;
176248590Smm	a->format_close = archive_write_v7tar_close;
177248590Smm	a->format_free = archive_write_v7tar_free;
178248590Smm	a->format_finish_entry = archive_write_v7tar_finish_entry;
179248590Smm	a->archive.archive_format = ARCHIVE_FORMAT_TAR;
180248590Smm	a->archive.archive_format_name = "tar (non-POSIX)";
181248590Smm	return (ARCHIVE_OK);
182248590Smm}
183248590Smm
184248590Smmstatic int
185248590Smmarchive_write_v7tar_options(struct archive_write *a, const char *key,
186248590Smm    const char *val)
187248590Smm{
188248590Smm	struct v7tar *v7tar = (struct v7tar *)a->format_data;
189248590Smm	int ret = ARCHIVE_FAILED;
190248590Smm
191248590Smm	if (strcmp(key, "hdrcharset")  == 0) {
192248590Smm		if (val == NULL || val[0] == 0)
193248590Smm			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
194248590Smm			    "%s: hdrcharset option needs a character-set name",
195248590Smm			    a->format_name);
196248590Smm		else {
197248590Smm			v7tar->opt_sconv = archive_string_conversion_to_charset(
198248590Smm			    &a->archive, val, 0);
199248590Smm			if (v7tar->opt_sconv != NULL)
200248590Smm				ret = ARCHIVE_OK;
201248590Smm			else
202248590Smm				ret = ARCHIVE_FATAL;
203248590Smm		}
204248590Smm		return (ret);
205248590Smm	}
206248590Smm
207248590Smm	/* Note: The "warn" return is just to inform the options
208248590Smm	 * supervisor that we didn't handle it.  It will generate
209248590Smm	 * a suitable error if no one used this option. */
210248590Smm	return (ARCHIVE_WARN);
211248590Smm}
212248590Smm
213248590Smmstatic int
214248590Smmarchive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry)
215248590Smm{
216248590Smm	char buff[512];
217248590Smm	int ret, ret2;
218248590Smm	struct v7tar *v7tar;
219248590Smm	struct archive_entry *entry_main;
220248590Smm	struct archive_string_conv *sconv;
221248590Smm
222248590Smm	v7tar = (struct v7tar *)a->format_data;
223248590Smm
224248590Smm	/* Setup default string conversion. */
225248590Smm	if (v7tar->opt_sconv == NULL) {
226248590Smm		if (!v7tar->init_default_conversion) {
227248590Smm			v7tar->sconv_default =
228248590Smm			    archive_string_default_conversion_for_write(
229248590Smm				&(a->archive));
230248590Smm			v7tar->init_default_conversion = 1;
231248590Smm		}
232248590Smm		sconv = v7tar->sconv_default;
233248590Smm	} else
234248590Smm		sconv = v7tar->opt_sconv;
235248590Smm
236248590Smm	/* Sanity check. */
237248590Smm	if (archive_entry_pathname(entry) == NULL) {
238248590Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
239248590Smm		    "Can't record entry in tar file without pathname");
240248590Smm		return (ARCHIVE_FAILED);
241248590Smm	}
242248590Smm
243248590Smm	/* Only regular files (not hardlinks) have data. */
244248590Smm	if (archive_entry_hardlink(entry) != NULL ||
245248590Smm	    archive_entry_symlink(entry) != NULL ||
246248590Smm	    !(archive_entry_filetype(entry) == AE_IFREG))
247248590Smm		archive_entry_set_size(entry, 0);
248248590Smm
249248590Smm	if (AE_IFDIR == archive_entry_filetype(entry)) {
250248590Smm		const char *p;
251248590Smm		size_t path_length;
252248590Smm		/*
253248590Smm		 * Ensure a trailing '/'.  Modify the entry so
254248590Smm		 * the client sees the change.
255248590Smm		 */
256248590Smm#if defined(_WIN32) && !defined(__CYGWIN__)
257248590Smm		const wchar_t *wp;
258248590Smm
259248590Smm		wp = archive_entry_pathname_w(entry);
260248590Smm		if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
261248590Smm			struct archive_wstring ws;
262248590Smm
263248590Smm			archive_string_init(&ws);
264248590Smm			path_length = wcslen(wp);
265248590Smm			if (archive_wstring_ensure(&ws,
266248590Smm			    path_length + 2) == NULL) {
267248590Smm				archive_set_error(&a->archive, ENOMEM,
268248590Smm				    "Can't allocate v7tar data");
269248590Smm				archive_wstring_free(&ws);
270248590Smm				return(ARCHIVE_FATAL);
271248590Smm			}
272248590Smm			/* Should we keep '\' ? */
273248590Smm			if (wp[path_length -1] == L'\\')
274248590Smm				path_length--;
275248590Smm			archive_wstrncpy(&ws, wp, path_length);
276248590Smm			archive_wstrappend_wchar(&ws, L'/');
277248590Smm			archive_entry_copy_pathname_w(entry, ws.s);
278248590Smm			archive_wstring_free(&ws);
279248590Smm			p = NULL;
280248590Smm		} else
281248590Smm#endif
282248590Smm			p = archive_entry_pathname(entry);
283248590Smm		/*
284248590Smm		 * On Windows, this is a backup operation just in
285248590Smm		 * case getting WCS failed. On POSIX, this is a
286248590Smm		 * normal operation.
287248590Smm		 */
288342361Smm		if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
289248590Smm			struct archive_string as;
290248590Smm
291248590Smm			archive_string_init(&as);
292248590Smm			path_length = strlen(p);
293248590Smm			if (archive_string_ensure(&as,
294248590Smm			    path_length + 2) == NULL) {
295248590Smm				archive_set_error(&a->archive, ENOMEM,
296248590Smm				    "Can't allocate v7tar data");
297248590Smm				archive_string_free(&as);
298248590Smm				return(ARCHIVE_FATAL);
299248590Smm			}
300248590Smm#if defined(_WIN32) && !defined(__CYGWIN__)
301248590Smm			/* NOTE: This might break the pathname
302248590Smm			 * if the current code page is CP932 and
303248590Smm			 * the pathname includes a character '\'
304248590Smm			 * as a part of its multibyte pathname. */
305248590Smm			if (p[strlen(p) -1] == '\\')
306248590Smm				path_length--;
307248590Smm			else
308248590Smm#endif
309248590Smm			archive_strncpy(&as, p, path_length);
310248590Smm			archive_strappend_char(&as, '/');
311248590Smm			archive_entry_copy_pathname(entry, as.s);
312248590Smm			archive_string_free(&as);
313248590Smm		}
314248590Smm	}
315248590Smm
316248590Smm#if defined(_WIN32) && !defined(__CYGWIN__)
317311042Smm	/* Make sure the path separators in pathname, hardlink and symlink
318248590Smm	 * are all slash '/', not the Windows path separator '\'. */
319248590Smm	entry_main = __la_win_entry_in_posix_pathseparator(entry);
320248590Smm	if (entry_main == NULL) {
321248590Smm		archive_set_error(&a->archive, ENOMEM,
322248590Smm		    "Can't allocate v7tar data");
323248590Smm		return(ARCHIVE_FATAL);
324248590Smm	}
325248590Smm	if (entry != entry_main)
326248590Smm		entry = entry_main;
327248590Smm	else
328248590Smm		entry_main = NULL;
329248590Smm#else
330248590Smm	entry_main = NULL;
331248590Smm#endif
332248590Smm	ret = format_header_v7tar(a, buff, entry, 1, sconv);
333248590Smm	if (ret < ARCHIVE_WARN) {
334344674Smm		archive_entry_free(entry_main);
335248590Smm		return (ret);
336248590Smm	}
337248590Smm	ret2 = __archive_write_output(a, buff, 512);
338248590Smm	if (ret2 < ARCHIVE_WARN) {
339344674Smm		archive_entry_free(entry_main);
340248590Smm		return (ret2);
341248590Smm	}
342248590Smm	if (ret2 < ret)
343248590Smm		ret = ret2;
344248590Smm
345248590Smm	v7tar->entry_bytes_remaining = archive_entry_size(entry);
346248590Smm	v7tar->entry_padding = 0x1ff & (-(int64_t)v7tar->entry_bytes_remaining);
347344674Smm	archive_entry_free(entry_main);
348248590Smm	return (ret);
349248590Smm}
350248590Smm
351248590Smm/*
352248590Smm * Format a basic 512-byte "v7tar" header.
353248590Smm *
354248590Smm * Returns -1 if format failed (due to field overflow).
355248590Smm * Note that this always formats as much of the header as possible.
356248590Smm * If "strict" is set to zero, it will extend numeric fields as
357248590Smm * necessary (overwriting terminators or using base-256 extensions).
358248590Smm *
359248590Smm */
360248590Smmstatic int
361248590Smmformat_header_v7tar(struct archive_write *a, char h[512],
362248590Smm    struct archive_entry *entry, int strict,
363248590Smm    struct archive_string_conv *sconv)
364248590Smm{
365248590Smm	unsigned int checksum;
366248590Smm	int i, r, ret;
367248590Smm	size_t copy_length;
368248590Smm	const char *p, *pp;
369248590Smm	int mytartype;
370248590Smm
371248590Smm	ret = 0;
372248590Smm	mytartype = -1;
373248590Smm	/*
374248590Smm	 * The "template header" already includes the "v7tar"
375248590Smm	 * signature, various end-of-field markers and other required
376248590Smm	 * elements.
377248590Smm	 */
378248590Smm	memcpy(h, &template_header, 512);
379248590Smm
380248590Smm	/*
381248590Smm	 * Because the block is already null-filled, and strings
382248590Smm	 * are allowed to exactly fill their destination (without null),
383248590Smm	 * I use memcpy(dest, src, strlen()) here a lot to copy strings.
384248590Smm	 */
385248590Smm	r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
386248590Smm	if (r != 0) {
387248590Smm		if (errno == ENOMEM) {
388248590Smm			archive_set_error(&a->archive, ENOMEM,
389248590Smm			    "Can't allocate memory for Pathname");
390248590Smm			return (ARCHIVE_FATAL);
391248590Smm		}
392248590Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
393248590Smm		    "Can't translate pathname '%s' to %s",
394248590Smm		    pp, archive_string_conversion_charset_name(sconv));
395248590Smm		ret = ARCHIVE_WARN;
396248590Smm	}
397248590Smm	if (strict && copy_length < V7TAR_name_size)
398248590Smm		memcpy(h + V7TAR_name_offset, pp, copy_length);
399248590Smm	else if (!strict && copy_length <= V7TAR_name_size)
400248590Smm		memcpy(h + V7TAR_name_offset, pp, copy_length);
401248590Smm	else {
402248590Smm		/* Prefix is too long. */
403248590Smm		archive_set_error(&a->archive, ENAMETOOLONG,
404248590Smm		    "Pathname too long");
405248590Smm		ret = ARCHIVE_FAILED;
406248590Smm	}
407248590Smm
408248590Smm	r = archive_entry_hardlink_l(entry, &p, &copy_length, sconv);
409248590Smm	if (r != 0) {
410248590Smm		if (errno == ENOMEM) {
411248590Smm			archive_set_error(&a->archive, ENOMEM,
412248590Smm			    "Can't allocate memory for Linkname");
413248590Smm			return (ARCHIVE_FATAL);
414248590Smm		}
415248590Smm		archive_set_error(&a->archive,
416248590Smm		    ARCHIVE_ERRNO_FILE_FORMAT,
417248590Smm		    "Can't translate linkname '%s' to %s",
418248590Smm		    p, archive_string_conversion_charset_name(sconv));
419248590Smm		ret = ARCHIVE_WARN;
420248590Smm	}
421248590Smm	if (copy_length > 0)
422248590Smm		mytartype = '1';
423248590Smm	else {
424248590Smm		r = archive_entry_symlink_l(entry, &p, &copy_length, sconv);
425248590Smm		if (r != 0) {
426248590Smm			if (errno == ENOMEM) {
427248590Smm				archive_set_error(&a->archive, ENOMEM,
428248590Smm				    "Can't allocate memory for Linkname");
429248590Smm				return (ARCHIVE_FATAL);
430248590Smm			}
431248590Smm			archive_set_error(&a->archive,
432248590Smm			    ARCHIVE_ERRNO_FILE_FORMAT,
433248590Smm			    "Can't translate linkname '%s' to %s",
434248590Smm			    p, archive_string_conversion_charset_name(sconv));
435248590Smm			ret = ARCHIVE_WARN;
436248590Smm		}
437248590Smm	}
438248590Smm	if (copy_length > 0) {
439248590Smm		if (copy_length >= V7TAR_linkname_size) {
440248590Smm			archive_set_error(&a->archive, ENAMETOOLONG,
441248590Smm			    "Link contents too long");
442248590Smm			ret = ARCHIVE_FAILED;
443248590Smm			copy_length = V7TAR_linkname_size;
444248590Smm		}
445248590Smm		memcpy(h + V7TAR_linkname_offset, p, copy_length);
446248590Smm	}
447248590Smm
448248590Smm	if (format_number(archive_entry_mode(entry) & 07777,
449248590Smm	    h + V7TAR_mode_offset, V7TAR_mode_size,
450248590Smm	    V7TAR_mode_max_size, strict)) {
451248590Smm		archive_set_error(&a->archive, ERANGE,
452248590Smm		    "Numeric mode too large");
453248590Smm		ret = ARCHIVE_FAILED;
454248590Smm	}
455248590Smm
456248590Smm	if (format_number(archive_entry_uid(entry),
457248590Smm	    h + V7TAR_uid_offset, V7TAR_uid_size, V7TAR_uid_max_size, strict)) {
458248590Smm		archive_set_error(&a->archive, ERANGE,
459248590Smm		    "Numeric user ID too large");
460248590Smm		ret = ARCHIVE_FAILED;
461248590Smm	}
462248590Smm
463248590Smm	if (format_number(archive_entry_gid(entry),
464248590Smm	    h + V7TAR_gid_offset, V7TAR_gid_size, V7TAR_gid_max_size, strict)) {
465248590Smm		archive_set_error(&a->archive, ERANGE,
466248590Smm		    "Numeric group ID too large");
467248590Smm		ret = ARCHIVE_FAILED;
468248590Smm	}
469248590Smm
470248590Smm	if (format_number(archive_entry_size(entry),
471248590Smm	    h + V7TAR_size_offset, V7TAR_size_size,
472248590Smm	    V7TAR_size_max_size, strict)) {
473248590Smm		archive_set_error(&a->archive, ERANGE,
474248590Smm		    "File size out of range");
475248590Smm		ret = ARCHIVE_FAILED;
476248590Smm	}
477248590Smm
478248590Smm	if (format_number(archive_entry_mtime(entry),
479248590Smm	    h + V7TAR_mtime_offset, V7TAR_mtime_size,
480248590Smm	    V7TAR_mtime_max_size, strict)) {
481248590Smm		archive_set_error(&a->archive, ERANGE,
482248590Smm		    "File modification time too large");
483248590Smm		ret = ARCHIVE_FAILED;
484248590Smm	}
485248590Smm
486248590Smm	if (mytartype >= 0) {
487248590Smm		h[V7TAR_typeflag_offset] = mytartype;
488248590Smm	} else {
489248590Smm		switch (archive_entry_filetype(entry)) {
490248590Smm		case AE_IFREG: case AE_IFDIR:
491248590Smm			break;
492248590Smm		case AE_IFLNK:
493248590Smm			h[V7TAR_typeflag_offset] = '2';
494248590Smm			break;
495248590Smm		default:
496358090Smm			/* AE_IFBLK, AE_IFCHR, AE_IFIFO, AE_IFSOCK
497358090Smm			 * and unknown */
498358090Smm			__archive_write_entry_filetype_unsupported(
499358090Smm			    &a->archive, entry, "v7tar");
500248590Smm			ret = ARCHIVE_FAILED;
501248590Smm		}
502248590Smm	}
503248590Smm
504248590Smm	checksum = 0;
505248590Smm	for (i = 0; i < 512; i++)
506248590Smm		checksum += 255 & (unsigned int)h[i];
507248590Smm	format_octal(checksum, h + V7TAR_checksum_offset, 6);
508248590Smm	/* Can't be pre-set in the template. */
509248590Smm	h[V7TAR_checksum_offset + 6] = '\0';
510248590Smm	return (ret);
511248590Smm}
512248590Smm
513248590Smm/*
514248590Smm * Format a number into a field, with some intelligence.
515248590Smm */
516248590Smmstatic int
517248590Smmformat_number(int64_t v, char *p, int s, int maxsize, int strict)
518248590Smm{
519248590Smm	int64_t limit;
520248590Smm
521248590Smm	limit = ((int64_t)1 << (s*3));
522248590Smm
523248590Smm	/* "Strict" only permits octal values with proper termination. */
524248590Smm	if (strict)
525248590Smm		return (format_octal(v, p, s));
526248590Smm
527248590Smm	/*
528248590Smm	 * In non-strict mode, we allow the number to overwrite one or
529248590Smm	 * more bytes of the field termination.  Even old tar
530248590Smm	 * implementations should be able to handle this with no
531248590Smm	 * problem.
532248590Smm	 */
533248590Smm	if (v >= 0) {
534248590Smm		while (s <= maxsize) {
535248590Smm			if (v < limit)
536248590Smm				return (format_octal(v, p, s));
537248590Smm			s++;
538248590Smm			limit <<= 3;
539248590Smm		}
540248590Smm	}
541248590Smm
542248590Smm	/* Base-256 can handle any number, positive or negative. */
543248590Smm	return (format_256(v, p, maxsize));
544248590Smm}
545248590Smm
546248590Smm/*
547248590Smm * Format a number into the specified field using base-256.
548248590Smm */
549248590Smmstatic int
550248590Smmformat_256(int64_t v, char *p, int s)
551248590Smm{
552248590Smm	p += s;
553248590Smm	while (s-- > 0) {
554248590Smm		*--p = (char)(v & 0xff);
555248590Smm		v >>= 8;
556248590Smm	}
557248590Smm	*p |= 0x80; /* Set the base-256 marker bit. */
558248590Smm	return (0);
559248590Smm}
560248590Smm
561248590Smm/*
562248590Smm * Format a number into the specified field.
563248590Smm */
564248590Smmstatic int
565248590Smmformat_octal(int64_t v, char *p, int s)
566248590Smm{
567248590Smm	int len;
568248590Smm
569248590Smm	len = s;
570248590Smm
571248590Smm	/* Octal values can't be negative, so use 0. */
572248590Smm	if (v < 0) {
573248590Smm		while (len-- > 0)
574248590Smm			*p++ = '0';
575248590Smm		return (-1);
576248590Smm	}
577248590Smm
578248590Smm	p += s;		/* Start at the end and work backwards. */
579248590Smm	while (s-- > 0) {
580248590Smm		*--p = (char)('0' + (v & 7));
581248590Smm		v >>= 3;
582248590Smm	}
583248590Smm
584248590Smm	if (v == 0)
585248590Smm		return (0);
586248590Smm
587248590Smm	/* If it overflowed, fill field with max value. */
588248590Smm	while (len-- > 0)
589248590Smm		*p++ = '7';
590248590Smm
591248590Smm	return (-1);
592248590Smm}
593248590Smm
594248590Smmstatic int
595248590Smmarchive_write_v7tar_close(struct archive_write *a)
596248590Smm{
597248590Smm	return (__archive_write_nulls(a, 512*2));
598248590Smm}
599248590Smm
600248590Smmstatic int
601248590Smmarchive_write_v7tar_free(struct archive_write *a)
602248590Smm{
603248590Smm	struct v7tar *v7tar;
604248590Smm
605248590Smm	v7tar = (struct v7tar *)a->format_data;
606248590Smm	free(v7tar);
607248590Smm	a->format_data = NULL;
608248590Smm	return (ARCHIVE_OK);
609248590Smm}
610248590Smm
611248590Smmstatic int
612248590Smmarchive_write_v7tar_finish_entry(struct archive_write *a)
613248590Smm{
614248590Smm	struct v7tar *v7tar;
615248590Smm	int ret;
616248590Smm
617248590Smm	v7tar = (struct v7tar *)a->format_data;
618248590Smm	ret = __archive_write_nulls(a,
619248590Smm	    (size_t)(v7tar->entry_bytes_remaining + v7tar->entry_padding));
620248590Smm	v7tar->entry_bytes_remaining = v7tar->entry_padding = 0;
621248590Smm	return (ret);
622248590Smm}
623248590Smm
624248590Smmstatic ssize_t
625248590Smmarchive_write_v7tar_data(struct archive_write *a, const void *buff, size_t s)
626248590Smm{
627248590Smm	struct v7tar *v7tar;
628248590Smm	int ret;
629248590Smm
630248590Smm	v7tar = (struct v7tar *)a->format_data;
631248590Smm	if (s > v7tar->entry_bytes_remaining)
632248590Smm		s = (size_t)v7tar->entry_bytes_remaining;
633248590Smm	ret = __archive_write_output(a, buff, s);
634248590Smm	v7tar->entry_bytes_remaining -= s;
635248590Smm	if (ret != ARCHIVE_OK)
636248590Smm		return (ret);
637248590Smm	return (s);
638248590Smm}
639