Blame libarchive/archive_write_set_format_gnutar.c

Packit 08bd4c
/*-
Packit 08bd4c
 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
Packit 08bd4c
 * Author: Jonas Gastal <jgastal@profusion.mobi>
Packit 08bd4c
 * Copyright (c) 2011-2012 Michihiro NAKAJIMA
Packit 08bd4c
 *
Packit 08bd4c
 * All rights reserved.
Packit 08bd4c
 *
Packit 08bd4c
 * Redistribution and use in source and binary forms, with or without
Packit 08bd4c
 * modification, are permitted provided that the following conditions
Packit 08bd4c
 * are met:
Packit 08bd4c
 * 1. Redistributions of source code must retain the above copyright
Packit 08bd4c
 *    notice, this list of conditions and the following disclaimer.
Packit 08bd4c
 * 2. Redistributions in binary form must reproduce the above copyright
Packit 08bd4c
 *    notice, this list of conditions and the following disclaimer in the
Packit 08bd4c
 *    documentation and/or other materials provided with the distribution.
Packit 08bd4c
 *
Packit 08bd4c
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
Packit 08bd4c
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Packit 08bd4c
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
Packit 08bd4c
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
Packit 08bd4c
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
Packit 08bd4c
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Packit 08bd4c
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Packit 08bd4c
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Packit 08bd4c
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
Packit 08bd4c
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit 08bd4c
 */
Packit 08bd4c
Packit 08bd4c
#include "archive_platform.h"
Packit 08bd4c
__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_gnu_tar.c 191579 2009-04-27 18:35:03Z gastal $");
Packit 08bd4c
Packit 08bd4c
Packit 08bd4c
#ifdef HAVE_ERRNO_H
Packit 08bd4c
#include <errno.h>
Packit 08bd4c
#endif
Packit 08bd4c
#include <stdio.h>
Packit 08bd4c
#ifdef HAVE_STDLIB_H
Packit 08bd4c
#include <stdlib.h>
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef HAVE_STRING_H
Packit 08bd4c
#include <string.h>
Packit 08bd4c
#endif
Packit 08bd4c
Packit 08bd4c
#include "archive.h"
Packit 08bd4c
#include "archive_entry.h"
Packit 08bd4c
#include "archive_entry_locale.h"
Packit 08bd4c
#include "archive_private.h"
Packit 08bd4c
#include "archive_write_private.h"
Packit 08bd4c
Packit 08bd4c
struct gnutar {
Packit 08bd4c
	uint64_t	entry_bytes_remaining;
Packit 08bd4c
	uint64_t	entry_padding;
Packit 08bd4c
	const char *	linkname;
Packit 08bd4c
	size_t		linkname_length;
Packit 08bd4c
	const char *	pathname;
Packit 08bd4c
	size_t		pathname_length;
Packit 08bd4c
	const char *	uname;
Packit 08bd4c
	size_t		uname_length;
Packit 08bd4c
	const char *	gname;
Packit 08bd4c
	size_t		gname_length;
Packit 08bd4c
	struct archive_string_conv *opt_sconv;
Packit 08bd4c
	struct archive_string_conv *sconv_default;
Packit 08bd4c
	int init_default_conversion;
Packit 08bd4c
};
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Define structure of GNU tar header.
Packit 08bd4c
 */
Packit 08bd4c
#define	GNUTAR_name_offset 0
Packit 08bd4c
#define	GNUTAR_name_size 100
Packit 08bd4c
#define	GNUTAR_mode_offset 100
Packit 08bd4c
#define	GNUTAR_mode_size 7
Packit 08bd4c
#define	GNUTAR_mode_max_size 8
Packit 08bd4c
#define	GNUTAR_uid_offset 108
Packit 08bd4c
#define	GNUTAR_uid_size 7
Packit 08bd4c
#define	GNUTAR_uid_max_size 8
Packit 08bd4c
#define	GNUTAR_gid_offset 116
Packit 08bd4c
#define	GNUTAR_gid_size 7
Packit 08bd4c
#define	GNUTAR_gid_max_size 8
Packit 08bd4c
#define	GNUTAR_size_offset 124
Packit 08bd4c
#define	GNUTAR_size_size 11
Packit 08bd4c
#define	GNUTAR_size_max_size 12
Packit 08bd4c
#define	GNUTAR_mtime_offset 136
Packit 08bd4c
#define	GNUTAR_mtime_size 11
Packit 08bd4c
#define	GNUTAR_mtime_max_size 11
Packit 08bd4c
#define	GNUTAR_checksum_offset 148
Packit 08bd4c
#define	GNUTAR_checksum_size 8
Packit 08bd4c
#define	GNUTAR_typeflag_offset 156
Packit 08bd4c
#define	GNUTAR_typeflag_size 1
Packit 08bd4c
#define	GNUTAR_linkname_offset 157
Packit 08bd4c
#define	GNUTAR_linkname_size 100
Packit 08bd4c
#define	GNUTAR_magic_offset 257
Packit 08bd4c
#define	GNUTAR_magic_size 6
Packit 08bd4c
#define	GNUTAR_version_offset 263
Packit 08bd4c
#define	GNUTAR_version_size 2
Packit 08bd4c
#define	GNUTAR_uname_offset 265
Packit 08bd4c
#define	GNUTAR_uname_size 32
Packit 08bd4c
#define	GNUTAR_gname_offset 297
Packit 08bd4c
#define	GNUTAR_gname_size 32
Packit 08bd4c
#define	GNUTAR_rdevmajor_offset 329
Packit 08bd4c
#define	GNUTAR_rdevmajor_size 6
Packit 08bd4c
#define	GNUTAR_rdevmajor_max_size 8
Packit 08bd4c
#define	GNUTAR_rdevminor_offset 337
Packit 08bd4c
#define	GNUTAR_rdevminor_size 6
Packit 08bd4c
#define	GNUTAR_rdevminor_max_size 8
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * A filled-in copy of the header for initialization.
Packit 08bd4c
 */
Packit 08bd4c
static const char template_header[] = {
Packit 08bd4c
	/* name: 100 bytes */
Packit 08bd4c
	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,
Packit 08bd4c
	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,
Packit 08bd4c
	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,
Packit 08bd4c
	0,0,0,0,
Packit 08bd4c
	/* Mode, null termination: 8 bytes */
Packit 08bd4c
	'0','0','0','0','0','0', '0','\0',
Packit 08bd4c
	/* uid, null termination: 8 bytes */
Packit 08bd4c
	'0','0','0','0','0','0', '0','\0',
Packit 08bd4c
	/* gid, null termination: 8 bytes */
Packit 08bd4c
	'0','0','0','0','0','0', '0','\0',
Packit 08bd4c
	/* size, space termination: 12 bytes */
Packit 08bd4c
	'0','0','0','0','0','0','0','0','0','0','0', '\0',
Packit 08bd4c
	/* mtime, space termination: 12 bytes */
Packit 08bd4c
	'0','0','0','0','0','0','0','0','0','0','0', '\0',
Packit 08bd4c
	/* Initial checksum value: 8 spaces */
Packit 08bd4c
	' ',' ',' ',' ',' ',' ',' ',' ',
Packit 08bd4c
	/* Typeflag: 1 byte */
Packit 08bd4c
	'0',			/* '0' = regular file */
Packit 08bd4c
	/* Linkname: 100 bytes */
Packit 08bd4c
	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,
Packit 08bd4c
	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,
Packit 08bd4c
	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,
Packit 08bd4c
	0,0,0,0,
Packit 08bd4c
	/* Magic: 8 bytes */
Packit 08bd4c
	'u','s','t','a','r',' ', ' ','\0',
Packit 08bd4c
	/* Uname: 32 bytes */
Packit 08bd4c
	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,
Packit 08bd4c
	/* Gname: 32 bytes */
Packit 08bd4c
	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,
Packit 08bd4c
	/* rdevmajor + null padding: 8 bytes */
Packit 08bd4c
	'\0','\0','\0','\0','\0','\0', '\0','\0',
Packit 08bd4c
	/* rdevminor + null padding: 8 bytes */
Packit 08bd4c
	'\0','\0','\0','\0','\0','\0', '\0','\0',
Packit 08bd4c
	/* Padding: 167 bytes */
Packit 08bd4c
	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,
Packit 08bd4c
	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,
Packit 08bd4c
	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,
Packit 08bd4c
	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,
Packit 08bd4c
	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,
Packit 08bd4c
	0,0,0,0,0,0,0
Packit 08bd4c
};
Packit 08bd4c
Packit 08bd4c
static int      archive_write_gnutar_options(struct archive_write *,
Packit 08bd4c
		    const char *, const char *);
Packit 08bd4c
static int	archive_format_gnutar_header(struct archive_write *, char h[512],
Packit 08bd4c
		    struct archive_entry *, int tartype);
Packit 08bd4c
static int      archive_write_gnutar_header(struct archive_write *,
Packit 08bd4c
		    struct archive_entry *entry);
Packit 08bd4c
static ssize_t	archive_write_gnutar_data(struct archive_write *a, const void *buff,
Packit 08bd4c
		    size_t s);
Packit 08bd4c
static int	archive_write_gnutar_free(struct archive_write *);
Packit 08bd4c
static int	archive_write_gnutar_close(struct archive_write *);
Packit 08bd4c
static int	archive_write_gnutar_finish_entry(struct archive_write *);
Packit 08bd4c
static int	format_256(int64_t, char *, int);
Packit 08bd4c
static int	format_number(int64_t, char *, int size, int maxsize);
Packit 08bd4c
static int	format_octal(int64_t, char *, int);
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Set output format to 'GNU tar' format.
Packit 08bd4c
 */
Packit 08bd4c
int
Packit 08bd4c
archive_write_set_format_gnutar(struct archive *_a)
Packit 08bd4c
{
Packit 08bd4c
	struct archive_write *a = (struct archive_write *)_a;
Packit 08bd4c
	struct gnutar *gnutar;
Packit 08bd4c
Packit 08bd4c
	gnutar = (struct gnutar *)calloc(1, sizeof(*gnutar));
Packit 08bd4c
	if (gnutar == NULL) {
Packit 08bd4c
		archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
		    "Can't allocate gnutar data");
Packit 08bd4c
		return (ARCHIVE_FATAL);
Packit 08bd4c
	}
Packit 08bd4c
	a->format_data = gnutar;
Packit 08bd4c
	a->format_name = "gnutar";
Packit 08bd4c
	a->format_options = archive_write_gnutar_options;
Packit 08bd4c
	a->format_write_header = archive_write_gnutar_header;
Packit 08bd4c
	a->format_write_data = archive_write_gnutar_data;
Packit 08bd4c
	a->format_close = archive_write_gnutar_close;
Packit 08bd4c
	a->format_free = archive_write_gnutar_free;
Packit 08bd4c
	a->format_finish_entry = archive_write_gnutar_finish_entry;
Packit 08bd4c
	a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
Packit 08bd4c
	a->archive.archive_format_name = "GNU tar";
Packit 08bd4c
	return (ARCHIVE_OK);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_gnutar_options(struct archive_write *a, const char *key,
Packit 08bd4c
    const char *val)
Packit 08bd4c
{
Packit 08bd4c
	struct gnutar *gnutar = (struct gnutar *)a->format_data;
Packit 08bd4c
	int ret = ARCHIVE_FAILED;
Packit 08bd4c
Packit 08bd4c
	if (strcmp(key, "hdrcharset")  == 0) {
Packit 08bd4c
		if (val == NULL || val[0] == 0)
Packit 08bd4c
			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
Packit 08bd4c
			    "%s: hdrcharset option needs a character-set name",
Packit 08bd4c
			    a->format_name);
Packit 08bd4c
		else {
Packit 08bd4c
			gnutar->opt_sconv = archive_string_conversion_to_charset(
Packit 08bd4c
			    &a->archive, val, 0);
Packit 08bd4c
			if (gnutar->opt_sconv != NULL)
Packit 08bd4c
				ret = ARCHIVE_OK;
Packit 08bd4c
			else
Packit 08bd4c
				ret = ARCHIVE_FATAL;
Packit 08bd4c
		}
Packit 08bd4c
		return (ret);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* Note: The "warn" return is just to inform the options
Packit 08bd4c
	 * supervisor that we didn't handle it.  It will generate
Packit 08bd4c
	 * a suitable error if no one used this option. */
Packit 08bd4c
	return (ARCHIVE_WARN);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_gnutar_close(struct archive_write *a)
Packit 08bd4c
{
Packit 08bd4c
	return (__archive_write_nulls(a, 512*2));
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_gnutar_free(struct archive_write *a)
Packit 08bd4c
{
Packit 08bd4c
	struct gnutar *gnutar;
Packit 08bd4c
Packit 08bd4c
	gnutar = (struct gnutar *)a->format_data;
Packit 08bd4c
	free(gnutar);
Packit 08bd4c
	a->format_data = NULL;
Packit 08bd4c
	return (ARCHIVE_OK);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_gnutar_finish_entry(struct archive_write *a)
Packit 08bd4c
{
Packit 08bd4c
	struct gnutar *gnutar;
Packit 08bd4c
	int ret;
Packit 08bd4c
Packit 08bd4c
	gnutar = (struct gnutar *)a->format_data;
Packit 08bd4c
	ret = __archive_write_nulls(a, (size_t)
Packit 08bd4c
	    (gnutar->entry_bytes_remaining + gnutar->entry_padding));
Packit 08bd4c
	gnutar->entry_bytes_remaining = gnutar->entry_padding = 0;
Packit 08bd4c
	return (ret);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static ssize_t
Packit 08bd4c
archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s)
Packit 08bd4c
{
Packit 08bd4c
	struct gnutar *gnutar;
Packit 08bd4c
	int ret;
Packit 08bd4c
Packit 08bd4c
	gnutar = (struct gnutar *)a->format_data;
Packit 08bd4c
	if (s > gnutar->entry_bytes_remaining)
Packit 08bd4c
		s = (size_t)gnutar->entry_bytes_remaining;
Packit 08bd4c
	ret = __archive_write_output(a, buff, s);
Packit 08bd4c
	gnutar->entry_bytes_remaining -= s;
Packit 08bd4c
	if (ret != ARCHIVE_OK)
Packit 08bd4c
		return (ret);
Packit 08bd4c
	return (s);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_gnutar_header(struct archive_write *a,
Packit 08bd4c
     struct archive_entry *entry)
Packit 08bd4c
{
Packit 08bd4c
	char buff[512];
Packit 08bd4c
	int r, ret, ret2 = ARCHIVE_OK;
Packit 08bd4c
	int tartype;
Packit 08bd4c
	struct gnutar *gnutar;
Packit 08bd4c
	struct archive_string_conv *sconv;
Packit 08bd4c
	struct archive_entry *entry_main;
Packit 08bd4c
Packit 08bd4c
	gnutar = (struct gnutar *)a->format_data;
Packit 08bd4c
Packit 08bd4c
	/* Setup default string conversion. */
Packit 08bd4c
	if (gnutar->opt_sconv == NULL) {
Packit 08bd4c
		if (!gnutar->init_default_conversion) {
Packit 08bd4c
			gnutar->sconv_default =
Packit 08bd4c
			    archive_string_default_conversion_for_write(
Packit 08bd4c
				&(a->archive));
Packit 08bd4c
			gnutar->init_default_conversion = 1;
Packit 08bd4c
		}
Packit 08bd4c
		sconv = gnutar->sconv_default;
Packit 08bd4c
	} else
Packit 08bd4c
		sconv = gnutar->opt_sconv;
Packit 08bd4c
Packit 08bd4c
	/* Only regular files (not hardlinks) have data. */
Packit 08bd4c
	if (archive_entry_hardlink(entry) != NULL ||
Packit 08bd4c
	    archive_entry_symlink(entry) != NULL ||
Packit 08bd4c
	    !(archive_entry_filetype(entry) == AE_IFREG))
Packit 08bd4c
		archive_entry_set_size(entry, 0);
Packit 08bd4c
Packit 08bd4c
	if (AE_IFDIR == archive_entry_filetype(entry)) {
Packit 08bd4c
		const char *p;
Packit 08bd4c
		size_t path_length;
Packit 08bd4c
		/*
Packit 08bd4c
		 * Ensure a trailing '/'.  Modify the entry so
Packit 08bd4c
		 * the client sees the change.
Packit 08bd4c
		 */
Packit 08bd4c
#if defined(_WIN32) && !defined(__CYGWIN__)
Packit 08bd4c
		const wchar_t *wp;
Packit 08bd4c
Packit 08bd4c
		wp = archive_entry_pathname_w(entry);
Packit 08bd4c
		if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
Packit 08bd4c
			struct archive_wstring ws;
Packit 08bd4c
Packit 08bd4c
			archive_string_init(&ws);
Packit 08bd4c
			path_length = wcslen(wp);
Packit 08bd4c
			if (archive_wstring_ensure(&ws,
Packit 08bd4c
			    path_length + 2) == NULL) {
Packit 08bd4c
				archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
				    "Can't allocate ustar data");
Packit 08bd4c
				archive_wstring_free(&ws);
Packit 08bd4c
				return(ARCHIVE_FATAL);
Packit 08bd4c
			}
Packit 08bd4c
			/* Should we keep '\' ? */
Packit 08bd4c
			if (wp[path_length -1] == L'\\')
Packit 08bd4c
				path_length--;
Packit 08bd4c
			archive_wstrncpy(&ws, wp, path_length);
Packit 08bd4c
			archive_wstrappend_wchar(&ws, L'/');
Packit 08bd4c
			archive_entry_copy_pathname_w(entry, ws.s);
Packit 08bd4c
			archive_wstring_free(&ws);
Packit 08bd4c
			p = NULL;
Packit 08bd4c
		} else
Packit 08bd4c
#endif
Packit 08bd4c
			p = archive_entry_pathname(entry);
Packit 08bd4c
		/*
Packit 08bd4c
		 * On Windows, this is a backup operation just in
Packit 08bd4c
		 * case getting WCS failed. On POSIX, this is a
Packit 08bd4c
		 * normal operation.
Packit 08bd4c
		 */
Packit 08bd4c
		if (p != NULL && p[strlen(p) - 1] != '/') {
Packit 08bd4c
			struct archive_string as;
Packit 08bd4c
Packit 08bd4c
			archive_string_init(&as);
Packit 08bd4c
			path_length = strlen(p);
Packit 08bd4c
			if (archive_string_ensure(&as,
Packit 08bd4c
			    path_length + 2) == NULL) {
Packit 08bd4c
				archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
				    "Can't allocate ustar data");
Packit 08bd4c
				archive_string_free(&as);
Packit 08bd4c
				return(ARCHIVE_FATAL);
Packit 08bd4c
			}
Packit 08bd4c
#if defined(_WIN32) && !defined(__CYGWIN__)
Packit 08bd4c
			/* NOTE: This might break the pathname
Packit 08bd4c
			 * if the current code page is CP932 and
Packit 08bd4c
			 * the pathname includes a character '\'
Packit 08bd4c
			 * as a part of its multibyte pathname. */
Packit 08bd4c
			if (p[strlen(p) -1] == '\\')
Packit 08bd4c
				path_length--;
Packit 08bd4c
			else
Packit 08bd4c
#endif
Packit 08bd4c
			archive_strncpy(&as, p, path_length);
Packit 08bd4c
			archive_strappend_char(&as, '/');
Packit 08bd4c
			archive_entry_copy_pathname(entry, as.s);
Packit 08bd4c
			archive_string_free(&as);
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
#if defined(_WIN32) && !defined(__CYGWIN__)
Packit 08bd4c
	/* Make sure the path separators in pathname, hardlink and symlink
Packit 08bd4c
	 * are all slash '/', not the Windows path separator '\'. */
Packit 08bd4c
	entry_main = __la_win_entry_in_posix_pathseparator(entry);
Packit 08bd4c
	if (entry_main == NULL) {
Packit 08bd4c
		archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
		    "Can't allocate ustar data");
Packit 08bd4c
		return(ARCHIVE_FATAL);
Packit 08bd4c
	}
Packit 08bd4c
	if (entry != entry_main)
Packit 08bd4c
		entry = entry_main;
Packit 08bd4c
	else
Packit 08bd4c
		entry_main = NULL;
Packit 08bd4c
#else
Packit 08bd4c
	entry_main = NULL;
Packit 08bd4c
#endif
Packit 08bd4c
	r = archive_entry_pathname_l(entry, &(gnutar->pathname),
Packit 08bd4c
	    &(gnutar->pathname_length), sconv);
Packit 08bd4c
	if (r != 0) {
Packit 08bd4c
		if (errno == ENOMEM) {
Packit 08bd4c
			archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
			    "Can't allocate memory for Pathame");
Packit 08bd4c
			ret = ARCHIVE_FATAL;
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
		}
Packit 08bd4c
		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
Packit 08bd4c
		    "Can't translate pathname '%s' to %s",
Packit 08bd4c
		    archive_entry_pathname(entry),
Packit 08bd4c
		    archive_string_conversion_charset_name(sconv));
Packit 08bd4c
		ret2 = ARCHIVE_WARN;
Packit 08bd4c
	}
Packit 08bd4c
	r = archive_entry_uname_l(entry, &(gnutar->uname),
Packit 08bd4c
	    &(gnutar->uname_length), sconv);
Packit 08bd4c
	if (r != 0) {
Packit 08bd4c
		if (errno == ENOMEM) {
Packit 08bd4c
			archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
			    "Can't allocate memory for Uname");
Packit 08bd4c
			ret = ARCHIVE_FATAL;
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
		}
Packit 08bd4c
		archive_set_error(&a->archive,
Packit 08bd4c
		    ARCHIVE_ERRNO_FILE_FORMAT,
Packit 08bd4c
		    "Can't translate uname '%s' to %s",
Packit 08bd4c
		    archive_entry_uname(entry),
Packit 08bd4c
		    archive_string_conversion_charset_name(sconv));
Packit 08bd4c
		ret2 = ARCHIVE_WARN;
Packit 08bd4c
	}
Packit 08bd4c
	r = archive_entry_gname_l(entry, &(gnutar->gname),
Packit 08bd4c
	    &(gnutar->gname_length), sconv);
Packit 08bd4c
	if (r != 0) {
Packit 08bd4c
		if (errno == ENOMEM) {
Packit 08bd4c
			archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
			    "Can't allocate memory for Gname");
Packit 08bd4c
			ret = ARCHIVE_FATAL;
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
		}
Packit 08bd4c
		archive_set_error(&a->archive,
Packit 08bd4c
		    ARCHIVE_ERRNO_FILE_FORMAT,
Packit 08bd4c
		    "Can't translate gname '%s' to %s",
Packit 08bd4c
		    archive_entry_gname(entry),
Packit 08bd4c
		    archive_string_conversion_charset_name(sconv));
Packit 08bd4c
		ret2 = ARCHIVE_WARN;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* If linkname is longer than 100 chars we need to add a 'K' header. */
Packit 08bd4c
	r = archive_entry_hardlink_l(entry, &(gnutar->linkname),
Packit 08bd4c
	    &(gnutar->linkname_length), sconv);
Packit 08bd4c
	if (r != 0) {
Packit 08bd4c
		if (errno == ENOMEM) {
Packit 08bd4c
			archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
			    "Can't allocate memory for Linkname");
Packit 08bd4c
			ret = ARCHIVE_FATAL;
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
		}
Packit 08bd4c
		archive_set_error(&a->archive,
Packit 08bd4c
		    ARCHIVE_ERRNO_FILE_FORMAT,
Packit 08bd4c
		    "Can't translate linkname '%s' to %s",
Packit 08bd4c
		    archive_entry_hardlink(entry),
Packit 08bd4c
		    archive_string_conversion_charset_name(sconv));
Packit 08bd4c
		ret2 = ARCHIVE_WARN;
Packit 08bd4c
	}
Packit 08bd4c
	if (gnutar->linkname_length == 0) {
Packit 08bd4c
		r = archive_entry_symlink_l(entry, &(gnutar->linkname),
Packit 08bd4c
		    &(gnutar->linkname_length), sconv);
Packit 08bd4c
		if (r != 0) {
Packit 08bd4c
			if (errno == ENOMEM) {
Packit 08bd4c
				archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
				    "Can't allocate memory for Linkname");
Packit 08bd4c
				ret = ARCHIVE_FATAL;
Packit 08bd4c
				goto exit_write_header;
Packit 08bd4c
			}
Packit 08bd4c
			archive_set_error(&a->archive,
Packit 08bd4c
			    ARCHIVE_ERRNO_FILE_FORMAT,
Packit 08bd4c
			    "Can't translate linkname '%s' to %s",
Packit 08bd4c
			    archive_entry_hardlink(entry),
Packit 08bd4c
			    archive_string_conversion_charset_name(sconv));
Packit 08bd4c
			ret2 = ARCHIVE_WARN;
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
	if (gnutar->linkname_length > GNUTAR_linkname_size) {
Packit 08bd4c
		size_t length = gnutar->linkname_length + 1;
Packit 08bd4c
		struct archive_entry *temp = archive_entry_new2(&a->archive);
Packit 08bd4c
Packit 08bd4c
		/* Uname/gname here don't really matter since no one reads them;
Packit 08bd4c
		 * these are the values that GNU tar happens to use on FreeBSD. */
Packit 08bd4c
		archive_entry_set_uname(temp, "root");
Packit 08bd4c
		archive_entry_set_gname(temp, "wheel");
Packit 08bd4c
Packit 08bd4c
		archive_entry_set_pathname(temp, "././@LongLink");
Packit 08bd4c
		archive_entry_set_size(temp, length);
Packit 08bd4c
		ret = archive_format_gnutar_header(a, buff, temp, 'K');
Packit 08bd4c
		archive_entry_free(temp);
Packit 08bd4c
		if (ret < ARCHIVE_WARN)
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
		ret = __archive_write_output(a, buff, 512);
Packit 08bd4c
		if (ret < ARCHIVE_WARN)
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
		/* Write name and trailing null byte. */
Packit 08bd4c
		ret = __archive_write_output(a, gnutar->linkname, length);
Packit 08bd4c
		if (ret < ARCHIVE_WARN)
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
		/* Pad to 512 bytes */
Packit 08bd4c
		ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));
Packit 08bd4c
		if (ret < ARCHIVE_WARN)
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* If pathname is longer than 100 chars we need to add an 'L' header. */
Packit 08bd4c
	if (gnutar->pathname_length > GNUTAR_name_size) {
Packit 08bd4c
		const char *pathname = gnutar->pathname;
Packit 08bd4c
		size_t length = gnutar->pathname_length + 1;
Packit 08bd4c
		struct archive_entry *temp = archive_entry_new2(&a->archive);
Packit 08bd4c
Packit 08bd4c
		/* Uname/gname here don't really matter since no one reads them;
Packit 08bd4c
		 * these are the values that GNU tar happens to use on FreeBSD. */
Packit 08bd4c
		archive_entry_set_uname(temp, "root");
Packit 08bd4c
		archive_entry_set_gname(temp, "wheel");
Packit 08bd4c
Packit 08bd4c
		archive_entry_set_pathname(temp, "././@LongLink");
Packit 08bd4c
		archive_entry_set_size(temp, length);
Packit 08bd4c
		ret = archive_format_gnutar_header(a, buff, temp, 'L');
Packit 08bd4c
		archive_entry_free(temp);
Packit 08bd4c
		if (ret < ARCHIVE_WARN)
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
		ret = __archive_write_output(a, buff, 512);
Packit 08bd4c
		if(ret < ARCHIVE_WARN)
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
		/* Write pathname + trailing null byte. */
Packit 08bd4c
		ret = __archive_write_output(a, pathname, length);
Packit 08bd4c
		if(ret < ARCHIVE_WARN)
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
		/* Pad to multiple of 512 bytes. */
Packit 08bd4c
		ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));
Packit 08bd4c
		if (ret < ARCHIVE_WARN)
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	if (archive_entry_hardlink(entry) != NULL) {
Packit 08bd4c
		tartype = '1';
Packit 08bd4c
	} else
Packit 08bd4c
		switch (archive_entry_filetype(entry)) {
Packit 08bd4c
		case AE_IFREG: tartype = '0' ; break;
Packit 08bd4c
		case AE_IFLNK: tartype = '2' ; break;
Packit 08bd4c
		case AE_IFCHR: tartype = '3' ; break;
Packit 08bd4c
		case AE_IFBLK: tartype = '4' ; break;
Packit 08bd4c
		case AE_IFDIR: tartype = '5' ; break;
Packit 08bd4c
		case AE_IFIFO: tartype = '6' ; break;
Packit 08bd4c
		case AE_IFSOCK:
Packit 08bd4c
			archive_set_error(&a->archive,
Packit 08bd4c
			    ARCHIVE_ERRNO_FILE_FORMAT,
Packit 08bd4c
			    "tar format cannot archive socket");
Packit 08bd4c
			ret = ARCHIVE_FAILED;
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
		default:
Packit 08bd4c
			archive_set_error(&a->archive,
Packit 08bd4c
			    ARCHIVE_ERRNO_FILE_FORMAT,
Packit 08bd4c
			    "tar format cannot archive this (mode=0%lo)",
Packit 08bd4c
			    (unsigned long)archive_entry_mode(entry));
Packit 08bd4c
			ret = ARCHIVE_FAILED;
Packit 08bd4c
			goto exit_write_header;
Packit 08bd4c
		}
Packit 08bd4c
Packit 08bd4c
	ret = archive_format_gnutar_header(a, buff, entry, tartype);
Packit 08bd4c
	if (ret < ARCHIVE_WARN)
Packit 08bd4c
		goto exit_write_header;
Packit 08bd4c
	if (ret2 < ret)
Packit 08bd4c
		ret = ret2;
Packit 08bd4c
	ret2 = __archive_write_output(a, buff, 512);
Packit 08bd4c
	if (ret2 < ARCHIVE_WARN) {
Packit 08bd4c
		ret = ret2;
Packit 08bd4c
		goto exit_write_header;
Packit 08bd4c
	}
Packit 08bd4c
	if (ret2 < ret)
Packit 08bd4c
		ret = ret2;
Packit 08bd4c
Packit 08bd4c
	gnutar->entry_bytes_remaining = archive_entry_size(entry);
Packit 08bd4c
	gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining);
Packit 08bd4c
exit_write_header:
Packit 08bd4c
	if (entry_main)
Packit 08bd4c
		archive_entry_free(entry_main);
Packit 08bd4c
	return (ret);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_format_gnutar_header(struct archive_write *a, char h[512],
Packit 08bd4c
    struct archive_entry *entry, int tartype)
Packit 08bd4c
{
Packit 08bd4c
	unsigned int checksum;
Packit 08bd4c
	int i, ret;
Packit 08bd4c
	size_t copy_length;
Packit 08bd4c
	const char *p;
Packit 08bd4c
	struct gnutar *gnutar;
Packit 08bd4c
Packit 08bd4c
	gnutar = (struct gnutar *)a->format_data;
Packit 08bd4c
Packit 08bd4c
	ret = 0;
Packit 08bd4c
Packit 08bd4c
	/*
Packit 08bd4c
	 * The "template header" already includes the signature,
Packit 08bd4c
	 * various end-of-field markers, and other required elements.
Packit 08bd4c
	 */
Packit 08bd4c
	memcpy(h, &template_header, 512);
Packit 08bd4c
Packit 08bd4c
	/*
Packit 08bd4c
	 * Because the block is already null-filled, and strings
Packit 08bd4c
	 * are allowed to exactly fill their destination (without null),
Packit 08bd4c
	 * I use memcpy(dest, src, strlen()) here a lot to copy strings.
Packit 08bd4c
	 */
Packit 08bd4c
Packit 08bd4c
	if (tartype == 'K' || tartype == 'L') {
Packit 08bd4c
		p = archive_entry_pathname(entry);
Packit 08bd4c
		copy_length = strlen(p);
Packit 08bd4c
	} else {
Packit 08bd4c
		p = gnutar->pathname;
Packit 08bd4c
		copy_length = gnutar->pathname_length;
Packit 08bd4c
	}
Packit 08bd4c
	if (copy_length > GNUTAR_name_size)
Packit 08bd4c
		copy_length = GNUTAR_name_size;
Packit 08bd4c
	memcpy(h + GNUTAR_name_offset, p, copy_length);
Packit 08bd4c
Packit 08bd4c
	if ((copy_length = gnutar->linkname_length) > 0) {
Packit 08bd4c
		if (copy_length > GNUTAR_linkname_size)
Packit 08bd4c
			copy_length = GNUTAR_linkname_size;
Packit 08bd4c
		memcpy(h + GNUTAR_linkname_offset, gnutar->linkname,
Packit 08bd4c
		    copy_length);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* TODO: How does GNU tar handle unames longer than GNUTAR_uname_size? */
Packit 08bd4c
	if (tartype == 'K' || tartype == 'L') {
Packit 08bd4c
		p = archive_entry_uname(entry);
Packit 08bd4c
		copy_length = strlen(p);
Packit 08bd4c
	} else {
Packit 08bd4c
		p = gnutar->uname;
Packit 08bd4c
		copy_length = gnutar->uname_length;
Packit 08bd4c
	}
Packit 08bd4c
	if (copy_length > 0) {
Packit 08bd4c
		if (copy_length > GNUTAR_uname_size)
Packit 08bd4c
			copy_length = GNUTAR_uname_size;
Packit 08bd4c
		memcpy(h + GNUTAR_uname_offset, p, copy_length);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* TODO: How does GNU tar handle gnames longer than GNUTAR_gname_size? */
Packit 08bd4c
	if (tartype == 'K' || tartype == 'L') {
Packit 08bd4c
		p = archive_entry_gname(entry);
Packit 08bd4c
		copy_length = strlen(p);
Packit 08bd4c
	} else {
Packit 08bd4c
		p = gnutar->gname;
Packit 08bd4c
		copy_length = gnutar->gname_length;
Packit 08bd4c
	}
Packit 08bd4c
	if (copy_length > 0) {
Packit 08bd4c
		if (strlen(p) > GNUTAR_gname_size)
Packit 08bd4c
			copy_length = GNUTAR_gname_size;
Packit 08bd4c
		memcpy(h + GNUTAR_gname_offset, p, copy_length);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* By truncating the mode here, we ensure it always fits. */
Packit 08bd4c
	format_octal(archive_entry_mode(entry) & 07777,
Packit 08bd4c
	    h + GNUTAR_mode_offset, GNUTAR_mode_size);
Packit 08bd4c
Packit 08bd4c
	/* GNU tar supports base-256 here, so should never overflow. */
Packit 08bd4c
	if (format_number(archive_entry_uid(entry), h + GNUTAR_uid_offset,
Packit 08bd4c
		GNUTAR_uid_size, GNUTAR_uid_max_size)) {
Packit 08bd4c
		archive_set_error(&a->archive, ERANGE,
Packit 08bd4c
		    "Numeric user ID %jd too large",
Packit 08bd4c
		    (intmax_t)archive_entry_uid(entry));
Packit 08bd4c
		ret = ARCHIVE_FAILED;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* GNU tar supports base-256 here, so should never overflow. */
Packit 08bd4c
	if (format_number(archive_entry_gid(entry), h + GNUTAR_gid_offset,
Packit 08bd4c
		GNUTAR_gid_size, GNUTAR_gid_max_size)) {
Packit 08bd4c
		archive_set_error(&a->archive, ERANGE,
Packit 08bd4c
		    "Numeric group ID %jd too large",
Packit 08bd4c
		    (intmax_t)archive_entry_gid(entry));
Packit 08bd4c
		ret = ARCHIVE_FAILED;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* GNU tar supports base-256 here, so should never overflow. */
Packit 08bd4c
	if (format_number(archive_entry_size(entry), h + GNUTAR_size_offset,
Packit 08bd4c
		GNUTAR_size_size, GNUTAR_size_max_size)) {
Packit 08bd4c
		archive_set_error(&a->archive, ERANGE,
Packit 08bd4c
		    "File size out of range");
Packit 08bd4c
		ret = ARCHIVE_FAILED;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* Shouldn't overflow before 2106, since mtime field is 33 bits. */
Packit 08bd4c
	format_octal(archive_entry_mtime(entry),
Packit 08bd4c
	    h + GNUTAR_mtime_offset, GNUTAR_mtime_size);
Packit 08bd4c
Packit 08bd4c
	if (archive_entry_filetype(entry) == AE_IFBLK
Packit 08bd4c
	    || archive_entry_filetype(entry) == AE_IFCHR) {
Packit 08bd4c
		if (format_octal(archive_entry_rdevmajor(entry),
Packit 08bd4c
		    h + GNUTAR_rdevmajor_offset,
Packit 08bd4c
			GNUTAR_rdevmajor_size)) {
Packit 08bd4c
			archive_set_error(&a->archive, ERANGE,
Packit 08bd4c
			    "Major device number too large");
Packit 08bd4c
			ret = ARCHIVE_FAILED;
Packit 08bd4c
		}
Packit 08bd4c
Packit 08bd4c
		if (format_octal(archive_entry_rdevminor(entry),
Packit 08bd4c
		    h + GNUTAR_rdevminor_offset,
Packit 08bd4c
			GNUTAR_rdevminor_size)) {
Packit 08bd4c
			archive_set_error(&a->archive, ERANGE,
Packit 08bd4c
			    "Minor device number too large");
Packit 08bd4c
			ret = ARCHIVE_FAILED;
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	h[GNUTAR_typeflag_offset] = tartype;
Packit 08bd4c
Packit 08bd4c
	checksum = 0;
Packit 08bd4c
	for (i = 0; i < 512; i++)
Packit 08bd4c
		checksum += 255 & (unsigned int)h[i];
Packit 08bd4c
	h[GNUTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */
Packit 08bd4c
	/* h[GNUTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */
Packit 08bd4c
	format_octal(checksum, h + GNUTAR_checksum_offset, 6);
Packit 08bd4c
	return (ret);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Format a number into a field, falling back to base-256 if necessary.
Packit 08bd4c
 */
Packit 08bd4c
static int
Packit 08bd4c
format_number(int64_t v, char *p, int s, int maxsize)
Packit 08bd4c
{
Packit 08bd4c
	int64_t limit = ((int64_t)1 << (s*3));
Packit 08bd4c
Packit 08bd4c
	if (v < limit)
Packit 08bd4c
		return (format_octal(v, p, s));
Packit 08bd4c
	return (format_256(v, p, maxsize));
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Format a number into the specified field using base-256.
Packit 08bd4c
 */
Packit 08bd4c
static int
Packit 08bd4c
format_256(int64_t v, char *p, int s)
Packit 08bd4c
{
Packit 08bd4c
	p += s;
Packit 08bd4c
	while (s-- > 0) {
Packit 08bd4c
		*--p = (char)(v & 0xff);
Packit 08bd4c
		v >>= 8;
Packit 08bd4c
	}
Packit 08bd4c
	*p |= 0x80; /* Set the base-256 marker bit. */
Packit 08bd4c
	return (0);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Format a number into the specified field using octal.
Packit 08bd4c
 */
Packit 08bd4c
static int
Packit 08bd4c
format_octal(int64_t v, char *p, int s)
Packit 08bd4c
{
Packit 08bd4c
	int len = s;
Packit 08bd4c
Packit 08bd4c
	/* Octal values can't be negative, so use 0. */
Packit 08bd4c
	if (v < 0)
Packit 08bd4c
		v = 0;
Packit 08bd4c
Packit 08bd4c
	p += s;		/* Start at the end and work backwards. */
Packit 08bd4c
	while (s-- > 0) {
Packit 08bd4c
		*--p = (char)('0' + (v & 7));
Packit 08bd4c
		v >>= 3;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	if (v == 0)
Packit 08bd4c
		return (0);
Packit 08bd4c
Packit 08bd4c
	/* If it overflowed, fill field with max value. */
Packit 08bd4c
	while (len-- > 0)
Packit 08bd4c
		*p++ = '7';
Packit 08bd4c
Packit 08bd4c
	return (-1);
Packit 08bd4c
}