Blame libarchive/archive_write_set_format_ustar.c

Packit 08bd4c
/*-
Packit 08bd4c
 * Copyright (c) 2003-2007 Tim Kientzle
Packit 08bd4c
 * Copyright (c) 2011-2012 Michihiro NAKAJIMA
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_ustar.c 191579 2009-04-27 18:35:03Z kientzle $");
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 ustar {
Packit 08bd4c
	uint64_t	entry_bytes_remaining;
Packit 08bd4c
	uint64_t	entry_padding;
Packit 08bd4c
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 POSIX 'ustar' tar header.
Packit 08bd4c
 */
Packit 08bd4c
#define	USTAR_name_offset 0
Packit 08bd4c
#define	USTAR_name_size 100
Packit 08bd4c
#define	USTAR_mode_offset 100
Packit 08bd4c
#define	USTAR_mode_size 6
Packit 08bd4c
#define	USTAR_mode_max_size 8
Packit 08bd4c
#define	USTAR_uid_offset 108
Packit 08bd4c
#define	USTAR_uid_size 6
Packit 08bd4c
#define	USTAR_uid_max_size 8
Packit 08bd4c
#define	USTAR_gid_offset 116
Packit 08bd4c
#define	USTAR_gid_size 6
Packit 08bd4c
#define	USTAR_gid_max_size 8
Packit 08bd4c
#define	USTAR_size_offset 124
Packit 08bd4c
#define	USTAR_size_size 11
Packit 08bd4c
#define	USTAR_size_max_size 12
Packit 08bd4c
#define	USTAR_mtime_offset 136
Packit 08bd4c
#define	USTAR_mtime_size 11
Packit 08bd4c
#define	USTAR_mtime_max_size 11
Packit 08bd4c
#define	USTAR_checksum_offset 148
Packit 08bd4c
#define	USTAR_checksum_size 8
Packit 08bd4c
#define	USTAR_typeflag_offset 156
Packit 08bd4c
#define	USTAR_typeflag_size 1
Packit 08bd4c
#define	USTAR_linkname_offset 157
Packit 08bd4c
#define	USTAR_linkname_size 100
Packit 08bd4c
#define	USTAR_magic_offset 257
Packit 08bd4c
#define	USTAR_magic_size 6
Packit 08bd4c
#define	USTAR_version_offset 263
Packit 08bd4c
#define	USTAR_version_size 2
Packit 08bd4c
#define	USTAR_uname_offset 265
Packit 08bd4c
#define	USTAR_uname_size 32
Packit 08bd4c
#define	USTAR_gname_offset 297
Packit 08bd4c
#define	USTAR_gname_size 32
Packit 08bd4c
#define	USTAR_rdevmajor_offset 329
Packit 08bd4c
#define	USTAR_rdevmajor_size 6
Packit 08bd4c
#define	USTAR_rdevmajor_max_size 8
Packit 08bd4c
#define	USTAR_rdevminor_offset 337
Packit 08bd4c
#define	USTAR_rdevminor_size 6
Packit 08bd4c
#define	USTAR_rdevminor_max_size 8
Packit 08bd4c
#define	USTAR_prefix_offset 345
Packit 08bd4c
#define	USTAR_prefix_size 155
Packit 08bd4c
#define	USTAR_padding_offset 500
Packit 08bd4c
#define	USTAR_padding_size 12
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, space-null termination: 8 bytes */
Packit 08bd4c
	'0','0','0','0','0','0', ' ','\0',
Packit 08bd4c
	/* uid, space-null termination: 8 bytes */
Packit 08bd4c
	'0','0','0','0','0','0', ' ','\0',
Packit 08bd4c
	/* gid, space-null termination: 8 bytes */
Packit 08bd4c
	'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', ' ',
Packit 08bd4c
	/* mtime, space termination: 12 bytes */
Packit 08bd4c
	'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: 6 bytes, Version: 2 bytes */
Packit 08bd4c
	'u','s','t','a','r','\0', '0','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 + space/null padding: 8 bytes */
Packit 08bd4c
	'0','0','0','0','0','0', ' ','\0',
Packit 08bd4c
	/* rdevminor + space/null padding: 8 bytes */
Packit 08bd4c
	'0','0','0','0','0','0', ' ','\0',
Packit 08bd4c
	/* Prefix: 155 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,
Packit 08bd4c
	/* Padding: 12 bytes */
Packit 08bd4c
	0,0,0,0,0,0,0,0, 0,0,0,0
Packit 08bd4c
};
Packit 08bd4c
Packit 08bd4c
static ssize_t	archive_write_ustar_data(struct archive_write *a, const void *buff,
Packit 08bd4c
		    size_t s);
Packit 08bd4c
static int	archive_write_ustar_free(struct archive_write *);
Packit 08bd4c
static int	archive_write_ustar_close(struct archive_write *);
Packit 08bd4c
static int	archive_write_ustar_finish_entry(struct archive_write *);
Packit 08bd4c
static int	archive_write_ustar_header(struct archive_write *,
Packit 08bd4c
		    struct archive_entry *entry);
Packit 08bd4c
static int	archive_write_ustar_options(struct archive_write *,
Packit 08bd4c
		    const char *, const char *);
Packit 08bd4c
static int	format_256(int64_t, char *, int);
Packit 08bd4c
static int	format_number(int64_t, char *, int size, int max, int strict);
Packit 08bd4c
static int	format_octal(int64_t, char *, int);
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Set output format to 'ustar' format.
Packit 08bd4c
 */
Packit 08bd4c
int
Packit 08bd4c
archive_write_set_format_ustar(struct archive *_a)
Packit 08bd4c
{
Packit 08bd4c
	struct archive_write *a = (struct archive_write *)_a;
Packit 08bd4c
	struct ustar *ustar;
Packit 08bd4c
Packit 08bd4c
	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
Packit 08bd4c
	    ARCHIVE_STATE_NEW, "archive_write_set_format_ustar");
Packit 08bd4c
Packit 08bd4c
	/* If someone else was already registered, unregister them. */
Packit 08bd4c
	if (a->format_free != NULL)
Packit 08bd4c
		(a->format_free)(a);
Packit 08bd4c
Packit 08bd4c
	/* Basic internal sanity test. */
Packit 08bd4c
	if (sizeof(template_header) != 512) {
Packit 08bd4c
		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
Packit 08bd4c
		    "Internal: template_header wrong size: %zu should be 512",
Packit 08bd4c
		    sizeof(template_header));
Packit 08bd4c
		return (ARCHIVE_FATAL);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	ustar = (struct ustar *)calloc(1, sizeof(*ustar));
Packit 08bd4c
	if (ustar == 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
	a->format_data = ustar;
Packit 08bd4c
	a->format_name = "ustar";
Packit 08bd4c
	a->format_options = archive_write_ustar_options;
Packit 08bd4c
	a->format_write_header = archive_write_ustar_header;
Packit 08bd4c
	a->format_write_data = archive_write_ustar_data;
Packit 08bd4c
	a->format_close = archive_write_ustar_close;
Packit 08bd4c
	a->format_free = archive_write_ustar_free;
Packit 08bd4c
	a->format_finish_entry = archive_write_ustar_finish_entry;
Packit 08bd4c
	a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
Packit 08bd4c
	a->archive.archive_format_name = "POSIX ustar";
Packit 08bd4c
	return (ARCHIVE_OK);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_ustar_options(struct archive_write *a, const char *key,
Packit 08bd4c
    const char *val)
Packit 08bd4c
{
Packit 08bd4c
	struct ustar *ustar = (struct ustar *)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
			ustar->opt_sconv = archive_string_conversion_to_charset(
Packit 08bd4c
			    &a->archive, val, 0);
Packit 08bd4c
			if (ustar->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_ustar_header(struct archive_write *a, struct archive_entry *entry)
Packit 08bd4c
{
Packit 08bd4c
	char buff[512];
Packit 08bd4c
	int ret, ret2;
Packit 08bd4c
	struct ustar *ustar;
Packit 08bd4c
	struct archive_entry *entry_main;
Packit 08bd4c
	struct archive_string_conv *sconv;
Packit 08bd4c
Packit 08bd4c
	ustar = (struct ustar *)a->format_data;
Packit 08bd4c
Packit 08bd4c
	/* Setup default string conversion. */
Packit 08bd4c
	if (ustar->opt_sconv == NULL) {
Packit 08bd4c
		if (!ustar->init_default_conversion) {
Packit 08bd4c
			ustar->sconv_default =
Packit 08bd4c
			    archive_string_default_conversion_for_write(&(a->archive));
Packit 08bd4c
			ustar->init_default_conversion = 1;
Packit 08bd4c
		}
Packit 08bd4c
		sconv = ustar->sconv_default;
Packit 08bd4c
	} else
Packit 08bd4c
		sconv = ustar->opt_sconv;
Packit 08bd4c
Packit 08bd4c
	/* Sanity check. */
Packit 08bd4c
	if (archive_entry_pathname(entry) == NULL) {
Packit 08bd4c
		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
Packit 08bd4c
		    "Can't record entry in tar file without pathname");
Packit 08bd4c
		return (ARCHIVE_FAILED);
Packit 08bd4c
	}
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[0] != '\0' && 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
	ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1, sconv);
Packit 08bd4c
	if (ret < ARCHIVE_WARN) {
Packit 08bd4c
		if (entry_main)
Packit 08bd4c
			archive_entry_free(entry_main);
Packit 08bd4c
		return (ret);
Packit 08bd4c
	}
Packit 08bd4c
	ret2 = __archive_write_output(a, buff, 512);
Packit 08bd4c
	if (ret2 < ARCHIVE_WARN) {
Packit 08bd4c
		if (entry_main)
Packit 08bd4c
			archive_entry_free(entry_main);
Packit 08bd4c
		return (ret2);
Packit 08bd4c
	}
Packit 08bd4c
	if (ret2 < ret)
Packit 08bd4c
		ret = ret2;
Packit 08bd4c
Packit 08bd4c
	ustar->entry_bytes_remaining = archive_entry_size(entry);
Packit 08bd4c
	ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining);
Packit 08bd4c
	if (entry_main)
Packit 08bd4c
		archive_entry_free(entry_main);
Packit 08bd4c
	return (ret);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Format a basic 512-byte "ustar" header.
Packit 08bd4c
 *
Packit 08bd4c
 * Returns -1 if format failed (due to field overflow).
Packit 08bd4c
 * Note that this always formats as much of the header as possible.
Packit 08bd4c
 * If "strict" is set to zero, it will extend numeric fields as
Packit 08bd4c
 * necessary (overwriting terminators or using base-256 extensions).
Packit 08bd4c
 *
Packit 08bd4c
 * This is exported so that other 'tar' formats can use it.
Packit 08bd4c
 */
Packit 08bd4c
int
Packit 08bd4c
__archive_write_format_header_ustar(struct archive_write *a, char h[512],
Packit 08bd4c
    struct archive_entry *entry, int tartype, int strict,
Packit 08bd4c
    struct archive_string_conv *sconv)
Packit 08bd4c
{
Packit 08bd4c
	unsigned int checksum;
Packit 08bd4c
	int i, r, ret;
Packit 08bd4c
	size_t copy_length;
Packit 08bd4c
	const char *p, *pp;
Packit 08bd4c
	int mytartype;
Packit 08bd4c
Packit 08bd4c
	ret = 0;
Packit 08bd4c
	mytartype = -1;
Packit 08bd4c
	/*
Packit 08bd4c
	 * The "template header" already includes the "ustar"
Packit 08bd4c
	 * signature, various end-of-field markers and other required
Packit 08bd4c
	 * 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
	r = archive_entry_pathname_l(entry, &pp, &copy_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 Pathname");
Packit 08bd4c
			return (ARCHIVE_FATAL);
Packit 08bd4c
		}
Packit 08bd4c
		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
Packit 08bd4c
		    "Can't translate pathname '%s' to %s",
Packit 08bd4c
		    pp, archive_string_conversion_charset_name(sconv));
Packit 08bd4c
		ret = ARCHIVE_WARN;
Packit 08bd4c
	}
Packit 08bd4c
	if (copy_length <= USTAR_name_size)
Packit 08bd4c
		memcpy(h + USTAR_name_offset, pp, copy_length);
Packit 08bd4c
	else {
Packit 08bd4c
		/* Store in two pieces, splitting at a '/'. */
Packit 08bd4c
		p = strchr(pp + copy_length - USTAR_name_size - 1, '/');
Packit 08bd4c
		/*
Packit 08bd4c
		 * Look for the next '/' if we chose the first character
Packit 08bd4c
		 * as the separator.  (ustar format doesn't permit
Packit 08bd4c
		 * an empty prefix.)
Packit 08bd4c
		 */
Packit 08bd4c
		if (p == pp)
Packit 08bd4c
			p = strchr(p + 1, '/');
Packit 08bd4c
		/* Fail if the name won't fit. */
Packit 08bd4c
		if (!p) {
Packit 08bd4c
			/* No separator. */
Packit 08bd4c
			archive_set_error(&a->archive, ENAMETOOLONG,
Packit 08bd4c
			    "Pathname too long");
Packit 08bd4c
			ret = ARCHIVE_FAILED;
Packit 08bd4c
		} else if (p[1] == '\0') {
Packit 08bd4c
			/*
Packit 08bd4c
			 * The only feasible separator is a final '/';
Packit 08bd4c
			 * this would result in a non-empty prefix and
Packit 08bd4c
			 * an empty name, which POSIX doesn't
Packit 08bd4c
			 * explicitly forbid, but it just feels wrong.
Packit 08bd4c
			 */
Packit 08bd4c
			archive_set_error(&a->archive, ENAMETOOLONG,
Packit 08bd4c
			    "Pathname too long");
Packit 08bd4c
			ret = ARCHIVE_FAILED;
Packit 08bd4c
		} else if (p  > pp + USTAR_prefix_size) {
Packit 08bd4c
			/* Prefix is too long. */
Packit 08bd4c
			archive_set_error(&a->archive, ENAMETOOLONG,
Packit 08bd4c
			    "Pathname too long");
Packit 08bd4c
			ret = ARCHIVE_FAILED;
Packit 08bd4c
		} else {
Packit 08bd4c
			/* Copy prefix and remainder to appropriate places */
Packit 08bd4c
			memcpy(h + USTAR_prefix_offset, pp, p - pp);
Packit 08bd4c
			memcpy(h + USTAR_name_offset, p + 1,
Packit 08bd4c
			    pp + copy_length - p - 1);
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	r = archive_entry_hardlink_l(entry, &p, &copy_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
			return (ARCHIVE_FATAL);
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
		    p, archive_string_conversion_charset_name(sconv));
Packit 08bd4c
		ret = ARCHIVE_WARN;
Packit 08bd4c
	}
Packit 08bd4c
	if (copy_length > 0)
Packit 08bd4c
		mytartype = '1';
Packit 08bd4c
	else {
Packit 08bd4c
		r = archive_entry_symlink_l(entry, &p, &copy_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
				return (ARCHIVE_FATAL);
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
			    p, archive_string_conversion_charset_name(sconv));
Packit 08bd4c
			ret = ARCHIVE_WARN;
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
	if (copy_length > 0) {
Packit 08bd4c
		if (copy_length > USTAR_linkname_size) {
Packit 08bd4c
			archive_set_error(&a->archive, ENAMETOOLONG,
Packit 08bd4c
			    "Link contents too long");
Packit 08bd4c
			ret = ARCHIVE_FAILED;
Packit 08bd4c
			copy_length = USTAR_linkname_size;
Packit 08bd4c
		}
Packit 08bd4c
		memcpy(h + USTAR_linkname_offset, p, copy_length);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	r = archive_entry_uname_l(entry, &p, &copy_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
			return (ARCHIVE_FATAL);
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
		    p, archive_string_conversion_charset_name(sconv));
Packit 08bd4c
		ret = ARCHIVE_WARN;
Packit 08bd4c
	}
Packit 08bd4c
	if (copy_length > 0) {
Packit 08bd4c
		if (copy_length > USTAR_uname_size) {
Packit 08bd4c
			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
Packit 08bd4c
			    "Username too long");
Packit 08bd4c
			ret = ARCHIVE_FAILED;
Packit 08bd4c
			copy_length = USTAR_uname_size;
Packit 08bd4c
		}
Packit 08bd4c
		memcpy(h + USTAR_uname_offset, p, copy_length);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	r = archive_entry_gname_l(entry, &p, &copy_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
			return (ARCHIVE_FATAL);
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
		    p, archive_string_conversion_charset_name(sconv));
Packit 08bd4c
		ret = ARCHIVE_WARN;
Packit 08bd4c
	}
Packit 08bd4c
	if (copy_length > 0) {
Packit 08bd4c
		if (strlen(p) > USTAR_gname_size) {
Packit 08bd4c
			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
Packit 08bd4c
			    "Group name too long");
Packit 08bd4c
			ret = ARCHIVE_FAILED;
Packit 08bd4c
			copy_length = USTAR_gname_size;
Packit 08bd4c
		}
Packit 08bd4c
		memcpy(h + USTAR_gname_offset, p, copy_length);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	if (format_number(archive_entry_mode(entry) & 07777,
Packit 08bd4c
	    h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) {
Packit 08bd4c
		archive_set_error(&a->archive, ERANGE,
Packit 08bd4c
		    "Numeric mode too large");
Packit 08bd4c
		ret = ARCHIVE_FAILED;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	if (format_number(archive_entry_uid(entry),
Packit 08bd4c
	    h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) {
Packit 08bd4c
		archive_set_error(&a->archive, ERANGE,
Packit 08bd4c
		    "Numeric user ID too large");
Packit 08bd4c
		ret = ARCHIVE_FAILED;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	if (format_number(archive_entry_gid(entry),
Packit 08bd4c
	    h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) {
Packit 08bd4c
		archive_set_error(&a->archive, ERANGE,
Packit 08bd4c
		    "Numeric group ID too large");
Packit 08bd4c
		ret = ARCHIVE_FAILED;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	if (format_number(archive_entry_size(entry),
Packit 08bd4c
	    h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) {
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
	if (format_number(archive_entry_mtime(entry),
Packit 08bd4c
	    h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) {
Packit 08bd4c
		archive_set_error(&a->archive, ERANGE,
Packit 08bd4c
		    "File modification time too large");
Packit 08bd4c
		ret = ARCHIVE_FAILED;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	if (archive_entry_filetype(entry) == AE_IFBLK
Packit 08bd4c
	    || archive_entry_filetype(entry) == AE_IFCHR) {
Packit 08bd4c
		if (format_number(archive_entry_rdevmajor(entry),
Packit 08bd4c
		    h + USTAR_rdevmajor_offset, USTAR_rdevmajor_size,
Packit 08bd4c
		    USTAR_rdevmajor_max_size, strict)) {
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_number(archive_entry_rdevminor(entry),
Packit 08bd4c
		    h + USTAR_rdevminor_offset, USTAR_rdevminor_size,
Packit 08bd4c
		    USTAR_rdevminor_max_size, strict)) {
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
	if (tartype >= 0) {
Packit 08bd4c
		h[USTAR_typeflag_offset] = tartype;
Packit 08bd4c
	} else if (mytartype >= 0) {
Packit 08bd4c
		h[USTAR_typeflag_offset] = mytartype;
Packit 08bd4c
	} else {
Packit 08bd4c
		switch (archive_entry_filetype(entry)) {
Packit 08bd4c
		case AE_IFREG: h[USTAR_typeflag_offset] = '0' ; break;
Packit 08bd4c
		case AE_IFLNK: h[USTAR_typeflag_offset] = '2' ; break;
Packit 08bd4c
		case AE_IFCHR: h[USTAR_typeflag_offset] = '3' ; break;
Packit 08bd4c
		case AE_IFBLK: h[USTAR_typeflag_offset] = '4' ; break;
Packit 08bd4c
		case AE_IFDIR: h[USTAR_typeflag_offset] = '5' ; break;
Packit 08bd4c
		case AE_IFIFO: h[USTAR_typeflag_offset] = '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
			return (ARCHIVE_FAILED);
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
		}
Packit 08bd4c
	}
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[USTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */
Packit 08bd4c
	/* h[USTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */
Packit 08bd4c
	format_octal(checksum, h + USTAR_checksum_offset, 6);
Packit 08bd4c
	return (ret);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Format a number into a field, with some intelligence.
Packit 08bd4c
 */
Packit 08bd4c
static int
Packit 08bd4c
format_number(int64_t v, char *p, int s, int maxsize, int strict)
Packit 08bd4c
{
Packit 08bd4c
	int64_t limit;
Packit 08bd4c
Packit 08bd4c
	limit = ((int64_t)1 << (s*3));
Packit 08bd4c
Packit 08bd4c
	/* "Strict" only permits octal values with proper termination. */
Packit 08bd4c
	if (strict)
Packit 08bd4c
		return (format_octal(v, p, s));
Packit 08bd4c
Packit 08bd4c
	/*
Packit 08bd4c
	 * In non-strict mode, we allow the number to overwrite one or
Packit 08bd4c
	 * more bytes of the field termination.  Even old tar
Packit 08bd4c
	 * implementations should be able to handle this with no
Packit 08bd4c
	 * problem.
Packit 08bd4c
	 */
Packit 08bd4c
	if (v >= 0) {
Packit 08bd4c
		while (s <= maxsize) {
Packit 08bd4c
			if (v < limit)
Packit 08bd4c
				return (format_octal(v, p, s));
Packit 08bd4c
			s++;
Packit 08bd4c
			limit <<= 3;
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* Base-256 can handle any number, positive or negative. */
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.
Packit 08bd4c
 */
Packit 08bd4c
static int
Packit 08bd4c
format_octal(int64_t v, char *p, int s)
Packit 08bd4c
{
Packit 08bd4c
	int len;
Packit 08bd4c
Packit 08bd4c
	len = s;
Packit 08bd4c
Packit 08bd4c
	/* Octal values can't be negative, so use 0. */
Packit 08bd4c
	if (v < 0) {
Packit 08bd4c
		while (len-- > 0)
Packit 08bd4c
			*p++ = '0';
Packit 08bd4c
		return (-1);
Packit 08bd4c
	}
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
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_ustar_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_ustar_free(struct archive_write *a)
Packit 08bd4c
{
Packit 08bd4c
	struct ustar *ustar;
Packit 08bd4c
Packit 08bd4c
	ustar = (struct ustar *)a->format_data;
Packit 08bd4c
	free(ustar);
Packit 08bd4c
	a->format_data = NULL;
Packit 08bd4c
	return (ARCHIVE_OK);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_ustar_finish_entry(struct archive_write *a)
Packit 08bd4c
{
Packit 08bd4c
	struct ustar *ustar;
Packit 08bd4c
	int ret;
Packit 08bd4c
Packit 08bd4c
	ustar = (struct ustar *)a->format_data;
Packit 08bd4c
	ret = __archive_write_nulls(a,
Packit 08bd4c
	    (size_t)(ustar->entry_bytes_remaining + ustar->entry_padding));
Packit 08bd4c
	ustar->entry_bytes_remaining = ustar->entry_padding = 0;
Packit 08bd4c
	return (ret);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static ssize_t
Packit 08bd4c
archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s)
Packit 08bd4c
{
Packit 08bd4c
	struct ustar *ustar;
Packit 08bd4c
	int ret;
Packit 08bd4c
Packit 08bd4c
	ustar = (struct ustar *)a->format_data;
Packit 08bd4c
	if (s > ustar->entry_bytes_remaining)
Packit 08bd4c
		s = (size_t)ustar->entry_bytes_remaining;
Packit 08bd4c
	ret = __archive_write_output(a, buff, s);
Packit 08bd4c
	ustar->entry_bytes_remaining -= s;
Packit 08bd4c
	if (ret != ARCHIVE_OK)
Packit 08bd4c
		return (ret);
Packit 08bd4c
	return (s);
Packit 08bd4c
}