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"
47248590Smm
48248590Smmstruct v7tar {
49248590Smm	uint64_t	entry_bytes_remaining;
50248590Smm	uint64_t	entry_padding;
51248590Smm
52248590Smm	struct archive_string_conv *opt_sconv;
53248590Smm	struct archive_string_conv *sconv_default;
54248590Smm	int	init_default_conversion;
55248590Smm};
56248590Smm
57248590Smm/*
58248590Smm * Define structure of POSIX 'v7tar' tar header.
59248590Smm */
60248590Smm#define	V7TAR_name_offset 0
61248590Smm#define	V7TAR_name_size 100
62248590Smm#define	V7TAR_mode_offset 100
63248590Smm#define	V7TAR_mode_size 6
64248590Smm#define	V7TAR_mode_max_size 8
65248590Smm#define	V7TAR_uid_offset 108
66248590Smm#define	V7TAR_uid_size 6
67248590Smm#define	V7TAR_uid_max_size 8
68248590Smm#define	V7TAR_gid_offset 116
69248590Smm#define	V7TAR_gid_size 6
70248590Smm#define	V7TAR_gid_max_size 8
71248590Smm#define	V7TAR_size_offset 124
72248590Smm#define	V7TAR_size_size 11
73248590Smm#define	V7TAR_size_max_size 12
74248590Smm#define	V7TAR_mtime_offset 136
75248590Smm#define	V7TAR_mtime_size 11
76248590Smm#define	V7TAR_mtime_max_size 12
77248590Smm#define	V7TAR_checksum_offset 148
78248590Smm#define	V7TAR_checksum_size 8
79248590Smm#define	V7TAR_typeflag_offset 156
80248590Smm#define	V7TAR_typeflag_size 1
81248590Smm#define	V7TAR_linkname_offset 157
82248590Smm#define	V7TAR_linkname_size 100
83248590Smm#define	V7TAR_padding_offset 257
84248590Smm#define	V7TAR_padding_size 255
85248590Smm
86248590Smm/*
87248590Smm * A filled-in copy of the header for initialization.
88248590Smm */
89248590Smmstatic const char template_header[] = {
90248590Smm	/* name: 100 bytes */
91248590Smm	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,
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,
95248590Smm	/* Mode, space-null termination: 8 bytes */
96248590Smm	'0','0','0','0','0','0', ' ','\0',
97248590Smm	/* uid, space-null termination: 8 bytes */
98248590Smm	'0','0','0','0','0','0', ' ','\0',
99248590Smm	/* gid, space-null termination: 8 bytes */
100248590Smm	'0','0','0','0','0','0', ' ','\0',
101248590Smm	/* size, space termation: 12 bytes */
102248590Smm	'0','0','0','0','0','0','0','0','0','0','0', ' ',
103248590Smm	/* mtime, space termation: 12 bytes */
104248590Smm	'0','0','0','0','0','0','0','0','0','0','0', ' ',
105248590Smm	/* Initial checksum value: 8 spaces */
106248590Smm	' ',' ',' ',' ',' ',' ',' ',' ',
107248590Smm	/* Typeflag: 1 byte */
108248590Smm	0,
109248590Smm	/* Linkname: 100 bytes */
110248590Smm	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,
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,
114248590Smm	/* Padding: 255 bytes */
115248590Smm	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,
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
123248590Smm};
124248590Smm
125248590Smmstatic ssize_t	archive_write_v7tar_data(struct archive_write *a, const void *buff,
126248590Smm		    size_t s);
127248590Smmstatic int	archive_write_v7tar_free(struct archive_write *);
128248590Smmstatic int	archive_write_v7tar_close(struct archive_write *);
129248590Smmstatic int	archive_write_v7tar_finish_entry(struct archive_write *);
130248590Smmstatic int	archive_write_v7tar_header(struct archive_write *,
131248590Smm		    struct archive_entry *entry);
132248590Smmstatic int	archive_write_v7tar_options(struct archive_write *,
133248590Smm		    const char *, const char *);
134248590Smmstatic int	format_256(int64_t, char *, int);
135248590Smmstatic int	format_number(int64_t, char *, int size, int max, int strict);
136248590Smmstatic int	format_octal(int64_t, char *, int);
137248590Smmstatic int	format_header_v7tar(struct archive_write *, char h[512],
138248590Smm		    struct archive_entry *, int, struct archive_string_conv *);
139248590Smm
140248590Smm/*
141248590Smm * Set output format to 'v7tar' format.
142248590Smm */
143248590Smmint
144248590Smmarchive_write_set_format_v7tar(struct archive *_a)
145248590Smm{
146248590Smm	struct archive_write *a = (struct archive_write *)_a;
147248590Smm	struct v7tar *v7tar;
148248590Smm
149248590Smm	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
150248590Smm	    ARCHIVE_STATE_NEW, "archive_write_set_format_v7tar");
151248590Smm
152248590Smm	/* If someone else was already registered, unregister them. */
153248590Smm	if (a->format_free != NULL)
154248590Smm		(a->format_free)(a);
155248590Smm
156248590Smm	/* Basic internal sanity test. */
157248590Smm	if (sizeof(template_header) != 512) {
158248590Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
159248590Smm		    "Internal: template_header wrong size: %zu should be 512",
160248590Smm		    sizeof(template_header));
161248590Smm		return (ARCHIVE_FATAL);
162248590Smm	}
163248590Smm
164248590Smm	v7tar = (struct v7tar *)malloc(sizeof(*v7tar));
165248590Smm	if (v7tar == NULL) {
166248590Smm		archive_set_error(&a->archive, ENOMEM,
167248590Smm		    "Can't allocate v7tar data");
168248590Smm		return (ARCHIVE_FATAL);
169248590Smm	}
170248590Smm	memset(v7tar, 0, sizeof(*v7tar));
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		 */
288248590Smm		if (p != NULL && 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__)
317248590Smm	/* Make sure the path separators in pahtname, 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) {
334248590Smm		if (entry_main)
335248590Smm			archive_entry_free(entry_main);
336248590Smm		return (ret);
337248590Smm	}
338248590Smm	ret2 = __archive_write_output(a, buff, 512);
339248590Smm	if (ret2 < ARCHIVE_WARN) {
340248590Smm		if (entry_main)
341248590Smm			archive_entry_free(entry_main);
342248590Smm		return (ret2);
343248590Smm	}
344248590Smm	if (ret2 < ret)
345248590Smm		ret = ret2;
346248590Smm
347248590Smm	v7tar->entry_bytes_remaining = archive_entry_size(entry);
348248590Smm	v7tar->entry_padding = 0x1ff & (-(int64_t)v7tar->entry_bytes_remaining);
349248590Smm	if (entry_main)
350248590Smm		archive_entry_free(entry_main);
351248590Smm	return (ret);
352248590Smm}
353248590Smm
354248590Smm/*
355248590Smm * Format a basic 512-byte "v7tar" header.
356248590Smm *
357248590Smm * Returns -1 if format failed (due to field overflow).
358248590Smm * Note that this always formats as much of the header as possible.
359248590Smm * If "strict" is set to zero, it will extend numeric fields as
360248590Smm * necessary (overwriting terminators or using base-256 extensions).
361248590Smm *
362248590Smm */
363248590Smmstatic int
364248590Smmformat_header_v7tar(struct archive_write *a, char h[512],
365248590Smm    struct archive_entry *entry, int strict,
366248590Smm    struct archive_string_conv *sconv)
367248590Smm{
368248590Smm	unsigned int checksum;
369248590Smm	int i, r, ret;
370248590Smm	size_t copy_length;
371248590Smm	const char *p, *pp;
372248590Smm	int mytartype;
373248590Smm
374248590Smm	ret = 0;
375248590Smm	mytartype = -1;
376248590Smm	/*
377248590Smm	 * The "template header" already includes the "v7tar"
378248590Smm	 * signature, various end-of-field markers and other required
379248590Smm	 * elements.
380248590Smm	 */
381248590Smm	memcpy(h, &template_header, 512);
382248590Smm
383248590Smm	/*
384248590Smm	 * Because the block is already null-filled, and strings
385248590Smm	 * are allowed to exactly fill their destination (without null),
386248590Smm	 * I use memcpy(dest, src, strlen()) here a lot to copy strings.
387248590Smm	 */
388248590Smm	r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
389248590Smm	if (r != 0) {
390248590Smm		if (errno == ENOMEM) {
391248590Smm			archive_set_error(&a->archive, ENOMEM,
392248590Smm			    "Can't allocate memory for Pathname");
393248590Smm			return (ARCHIVE_FATAL);
394248590Smm		}
395248590Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
396248590Smm		    "Can't translate pathname '%s' to %s",
397248590Smm		    pp, archive_string_conversion_charset_name(sconv));
398248590Smm		ret = ARCHIVE_WARN;
399248590Smm	}
400248590Smm	if (strict && copy_length < V7TAR_name_size)
401248590Smm		memcpy(h + V7TAR_name_offset, pp, copy_length);
402248590Smm	else if (!strict && copy_length <= V7TAR_name_size)
403248590Smm		memcpy(h + V7TAR_name_offset, pp, copy_length);
404248590Smm	else {
405248590Smm		/* Prefix is too long. */
406248590Smm		archive_set_error(&a->archive, ENAMETOOLONG,
407248590Smm		    "Pathname too long");
408248590Smm		ret = ARCHIVE_FAILED;
409248590Smm	}
410248590Smm
411248590Smm	r = archive_entry_hardlink_l(entry, &p, &copy_length, sconv);
412248590Smm	if (r != 0) {
413248590Smm		if (errno == ENOMEM) {
414248590Smm			archive_set_error(&a->archive, ENOMEM,
415248590Smm			    "Can't allocate memory for Linkname");
416248590Smm			return (ARCHIVE_FATAL);
417248590Smm		}
418248590Smm		archive_set_error(&a->archive,
419248590Smm		    ARCHIVE_ERRNO_FILE_FORMAT,
420248590Smm		    "Can't translate linkname '%s' to %s",
421248590Smm		    p, archive_string_conversion_charset_name(sconv));
422248590Smm		ret = ARCHIVE_WARN;
423248590Smm	}
424248590Smm	if (copy_length > 0)
425248590Smm		mytartype = '1';
426248590Smm	else {
427248590Smm		r = archive_entry_symlink_l(entry, &p, &copy_length, sconv);
428248590Smm		if (r != 0) {
429248590Smm			if (errno == ENOMEM) {
430248590Smm				archive_set_error(&a->archive, ENOMEM,
431248590Smm				    "Can't allocate memory for Linkname");
432248590Smm				return (ARCHIVE_FATAL);
433248590Smm			}
434248590Smm			archive_set_error(&a->archive,
435248590Smm			    ARCHIVE_ERRNO_FILE_FORMAT,
436248590Smm			    "Can't translate linkname '%s' to %s",
437248590Smm			    p, archive_string_conversion_charset_name(sconv));
438248590Smm			ret = ARCHIVE_WARN;
439248590Smm		}
440248590Smm	}
441248590Smm	if (copy_length > 0) {
442248590Smm		if (copy_length >= V7TAR_linkname_size) {
443248590Smm			archive_set_error(&a->archive, ENAMETOOLONG,
444248590Smm			    "Link contents too long");
445248590Smm			ret = ARCHIVE_FAILED;
446248590Smm			copy_length = V7TAR_linkname_size;
447248590Smm		}
448248590Smm		memcpy(h + V7TAR_linkname_offset, p, copy_length);
449248590Smm	}
450248590Smm
451248590Smm	if (format_number(archive_entry_mode(entry) & 07777,
452248590Smm	    h + V7TAR_mode_offset, V7TAR_mode_size,
453248590Smm	    V7TAR_mode_max_size, strict)) {
454248590Smm		archive_set_error(&a->archive, ERANGE,
455248590Smm		    "Numeric mode too large");
456248590Smm		ret = ARCHIVE_FAILED;
457248590Smm	}
458248590Smm
459248590Smm	if (format_number(archive_entry_uid(entry),
460248590Smm	    h + V7TAR_uid_offset, V7TAR_uid_size, V7TAR_uid_max_size, strict)) {
461248590Smm		archive_set_error(&a->archive, ERANGE,
462248590Smm		    "Numeric user ID too large");
463248590Smm		ret = ARCHIVE_FAILED;
464248590Smm	}
465248590Smm
466248590Smm	if (format_number(archive_entry_gid(entry),
467248590Smm	    h + V7TAR_gid_offset, V7TAR_gid_size, V7TAR_gid_max_size, strict)) {
468248590Smm		archive_set_error(&a->archive, ERANGE,
469248590Smm		    "Numeric group ID too large");
470248590Smm		ret = ARCHIVE_FAILED;
471248590Smm	}
472248590Smm
473248590Smm	if (format_number(archive_entry_size(entry),
474248590Smm	    h + V7TAR_size_offset, V7TAR_size_size,
475248590Smm	    V7TAR_size_max_size, strict)) {
476248590Smm		archive_set_error(&a->archive, ERANGE,
477248590Smm		    "File size out of range");
478248590Smm		ret = ARCHIVE_FAILED;
479248590Smm	}
480248590Smm
481248590Smm	if (format_number(archive_entry_mtime(entry),
482248590Smm	    h + V7TAR_mtime_offset, V7TAR_mtime_size,
483248590Smm	    V7TAR_mtime_max_size, strict)) {
484248590Smm		archive_set_error(&a->archive, ERANGE,
485248590Smm		    "File modification time too large");
486248590Smm		ret = ARCHIVE_FAILED;
487248590Smm	}
488248590Smm
489248590Smm	if (mytartype >= 0) {
490248590Smm		h[V7TAR_typeflag_offset] = mytartype;
491248590Smm	} else {
492248590Smm		switch (archive_entry_filetype(entry)) {
493248590Smm		case AE_IFREG: case AE_IFDIR:
494248590Smm			break;
495248590Smm		case AE_IFLNK:
496248590Smm			h[V7TAR_typeflag_offset] = '2';
497248590Smm			break;
498248590Smm		case AE_IFCHR:
499248590Smm			archive_set_error(&a->archive,
500248590Smm			    ARCHIVE_ERRNO_FILE_FORMAT,
501248590Smm			    "tar format cannot archive character device");
502248590Smm			return (ARCHIVE_FAILED);
503248590Smm		case AE_IFBLK:
504248590Smm			archive_set_error(&a->archive,
505248590Smm			    ARCHIVE_ERRNO_FILE_FORMAT,
506248590Smm			    "tar format cannot archive block device");
507248590Smm			return (ARCHIVE_FAILED);
508248590Smm		case AE_IFIFO:
509248590Smm			archive_set_error(&a->archive,
510248590Smm			    ARCHIVE_ERRNO_FILE_FORMAT,
511248590Smm			    "tar format cannot archive fifo");
512248590Smm			return (ARCHIVE_FAILED);
513248590Smm		case AE_IFSOCK:
514248590Smm			archive_set_error(&a->archive,
515248590Smm			    ARCHIVE_ERRNO_FILE_FORMAT,
516248590Smm			    "tar format cannot archive socket");
517248590Smm			return (ARCHIVE_FAILED);
518248590Smm		default:
519248590Smm			archive_set_error(&a->archive,
520248590Smm			    ARCHIVE_ERRNO_FILE_FORMAT,
521248590Smm			    "tar format cannot archive this (mode=0%lo)",
522248590Smm			    (unsigned long)archive_entry_mode(entry));
523248590Smm			ret = ARCHIVE_FAILED;
524248590Smm		}
525248590Smm	}
526248590Smm
527248590Smm	checksum = 0;
528248590Smm	for (i = 0; i < 512; i++)
529248590Smm		checksum += 255 & (unsigned int)h[i];
530248590Smm	format_octal(checksum, h + V7TAR_checksum_offset, 6);
531248590Smm	/* Can't be pre-set in the template. */
532248590Smm	h[V7TAR_checksum_offset + 6] = '\0';
533248590Smm	return (ret);
534248590Smm}
535248590Smm
536248590Smm/*
537248590Smm * Format a number into a field, with some intelligence.
538248590Smm */
539248590Smmstatic int
540248590Smmformat_number(int64_t v, char *p, int s, int maxsize, int strict)
541248590Smm{
542248590Smm	int64_t limit;
543248590Smm
544248590Smm	limit = ((int64_t)1 << (s*3));
545248590Smm
546248590Smm	/* "Strict" only permits octal values with proper termination. */
547248590Smm	if (strict)
548248590Smm		return (format_octal(v, p, s));
549248590Smm
550248590Smm	/*
551248590Smm	 * In non-strict mode, we allow the number to overwrite one or
552248590Smm	 * more bytes of the field termination.  Even old tar
553248590Smm	 * implementations should be able to handle this with no
554248590Smm	 * problem.
555248590Smm	 */
556248590Smm	if (v >= 0) {
557248590Smm		while (s <= maxsize) {
558248590Smm			if (v < limit)
559248590Smm				return (format_octal(v, p, s));
560248590Smm			s++;
561248590Smm			limit <<= 3;
562248590Smm		}
563248590Smm	}
564248590Smm
565248590Smm	/* Base-256 can handle any number, positive or negative. */
566248590Smm	return (format_256(v, p, maxsize));
567248590Smm}
568248590Smm
569248590Smm/*
570248590Smm * Format a number into the specified field using base-256.
571248590Smm */
572248590Smmstatic int
573248590Smmformat_256(int64_t v, char *p, int s)
574248590Smm{
575248590Smm	p += s;
576248590Smm	while (s-- > 0) {
577248590Smm		*--p = (char)(v & 0xff);
578248590Smm		v >>= 8;
579248590Smm	}
580248590Smm	*p |= 0x80; /* Set the base-256 marker bit. */
581248590Smm	return (0);
582248590Smm}
583248590Smm
584248590Smm/*
585248590Smm * Format a number into the specified field.
586248590Smm */
587248590Smmstatic int
588248590Smmformat_octal(int64_t v, char *p, int s)
589248590Smm{
590248590Smm	int len;
591248590Smm
592248590Smm	len = s;
593248590Smm
594248590Smm	/* Octal values can't be negative, so use 0. */
595248590Smm	if (v < 0) {
596248590Smm		while (len-- > 0)
597248590Smm			*p++ = '0';
598248590Smm		return (-1);
599248590Smm	}
600248590Smm
601248590Smm	p += s;		/* Start at the end and work backwards. */
602248590Smm	while (s-- > 0) {
603248590Smm		*--p = (char)('0' + (v & 7));
604248590Smm		v >>= 3;
605248590Smm	}
606248590Smm
607248590Smm	if (v == 0)
608248590Smm		return (0);
609248590Smm
610248590Smm	/* If it overflowed, fill field with max value. */
611248590Smm	while (len-- > 0)
612248590Smm		*p++ = '7';
613248590Smm
614248590Smm	return (-1);
615248590Smm}
616248590Smm
617248590Smmstatic int
618248590Smmarchive_write_v7tar_close(struct archive_write *a)
619248590Smm{
620248590Smm	return (__archive_write_nulls(a, 512*2));
621248590Smm}
622248590Smm
623248590Smmstatic int
624248590Smmarchive_write_v7tar_free(struct archive_write *a)
625248590Smm{
626248590Smm	struct v7tar *v7tar;
627248590Smm
628248590Smm	v7tar = (struct v7tar *)a->format_data;
629248590Smm	free(v7tar);
630248590Smm	a->format_data = NULL;
631248590Smm	return (ARCHIVE_OK);
632248590Smm}
633248590Smm
634248590Smmstatic int
635248590Smmarchive_write_v7tar_finish_entry(struct archive_write *a)
636248590Smm{
637248590Smm	struct v7tar *v7tar;
638248590Smm	int ret;
639248590Smm
640248590Smm	v7tar = (struct v7tar *)a->format_data;
641248590Smm	ret = __archive_write_nulls(a,
642248590Smm	    (size_t)(v7tar->entry_bytes_remaining + v7tar->entry_padding));
643248590Smm	v7tar->entry_bytes_remaining = v7tar->entry_padding = 0;
644248590Smm	return (ret);
645248590Smm}
646248590Smm
647248590Smmstatic ssize_t
648248590Smmarchive_write_v7tar_data(struct archive_write *a, const void *buff, size_t s)
649248590Smm{
650248590Smm	struct v7tar *v7tar;
651248590Smm	int ret;
652248590Smm
653248590Smm	v7tar = (struct v7tar *)a->format_data;
654248590Smm	if (s > v7tar->entry_bytes_remaining)
655248590Smm		s = (size_t)v7tar->entry_bytes_remaining;
656248590Smm	ret = __archive_write_output(a, buff, s);
657248590Smm	v7tar->entry_bytes_remaining -= s;
658248590Smm	if (ret != ARCHIVE_OK)
659248590Smm		return (ret);
660248590Smm	return (s);
661248590Smm}
662