Blame libarchive/archive_write_set_format_v7tar.c

Packit Service 1d0348
/*-
Packit Service 1d0348
 * Copyright (c) 2003-2007 Tim Kientzle
Packit Service 1d0348
 * Copyright (c) 2011-2012 Michihiro NAKAJIMA
Packit Service 1d0348
 * All rights reserved.
Packit Service 1d0348
 *
Packit Service 1d0348
 * Redistribution and use in source and binary forms, with or without
Packit Service 1d0348
 * modification, are permitted provided that the following conditions
Packit Service 1d0348
 * are met:
Packit Service 1d0348
 * 1. Redistributions of source code must retain the above copyright
Packit Service 1d0348
 *    notice, this list of conditions and the following disclaimer.
Packit Service 1d0348
 * 2. Redistributions in binary form must reproduce the above copyright
Packit Service 1d0348
 *    notice, this list of conditions and the following disclaimer in the
Packit Service 1d0348
 *    documentation and/or other materials provided with the distribution.
Packit Service 1d0348
 *
Packit Service 1d0348
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
Packit Service 1d0348
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Packit Service 1d0348
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
Packit Service 1d0348
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
Packit Service 1d0348
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
Packit Service 1d0348
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Packit Service 1d0348
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Packit Service 1d0348
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Packit Service 1d0348
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
Packit Service 1d0348
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit Service 1d0348
 */
Packit Service 1d0348
Packit Service 1d0348
#include "archive_platform.h"
Packit Service 1d0348
__FBSDID("$FreeBSD$");
Packit Service 1d0348
Packit Service 1d0348
Packit Service 1d0348
#ifdef HAVE_ERRNO_H
Packit Service 1d0348
#include <errno.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#include <stdio.h>
Packit Service 1d0348
#ifdef HAVE_STDLIB_H
Packit Service 1d0348
#include <stdlib.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#ifdef HAVE_STRING_H
Packit Service 1d0348
#include <string.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
Packit Service 1d0348
#include "archive.h"
Packit Service 1d0348
#include "archive_entry.h"
Packit Service 1d0348
#include "archive_entry_locale.h"
Packit Service 1d0348
#include "archive_private.h"
Packit Service 1d0348
#include "archive_write_private.h"
Packit Service 1d0348
Packit Service 1d0348
struct v7tar {
Packit Service 1d0348
	uint64_t	entry_bytes_remaining;
Packit Service 1d0348
	uint64_t	entry_padding;
Packit Service 1d0348
Packit Service 1d0348
	struct archive_string_conv *opt_sconv;
Packit Service 1d0348
	struct archive_string_conv *sconv_default;
Packit Service 1d0348
	int	init_default_conversion;
Packit Service 1d0348
};
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Define structure of POSIX 'v7tar' tar header.
Packit Service 1d0348
 */
Packit Service 1d0348
#define	V7TAR_name_offset 0
Packit Service 1d0348
#define	V7TAR_name_size 100
Packit Service 1d0348
#define	V7TAR_mode_offset 100
Packit Service 1d0348
#define	V7TAR_mode_size 6
Packit Service 1d0348
#define	V7TAR_mode_max_size 8
Packit Service 1d0348
#define	V7TAR_uid_offset 108
Packit Service 1d0348
#define	V7TAR_uid_size 6
Packit Service 1d0348
#define	V7TAR_uid_max_size 8
Packit Service 1d0348
#define	V7TAR_gid_offset 116
Packit Service 1d0348
#define	V7TAR_gid_size 6
Packit Service 1d0348
#define	V7TAR_gid_max_size 8
Packit Service 1d0348
#define	V7TAR_size_offset 124
Packit Service 1d0348
#define	V7TAR_size_size 11
Packit Service 1d0348
#define	V7TAR_size_max_size 12
Packit Service 1d0348
#define	V7TAR_mtime_offset 136
Packit Service 1d0348
#define	V7TAR_mtime_size 11
Packit Service 1d0348
#define	V7TAR_mtime_max_size 12
Packit Service 1d0348
#define	V7TAR_checksum_offset 148
Packit Service 1d0348
#define	V7TAR_checksum_size 8
Packit Service 1d0348
#define	V7TAR_typeflag_offset 156
Packit Service 1d0348
#define	V7TAR_typeflag_size 1
Packit Service 1d0348
#define	V7TAR_linkname_offset 157
Packit Service 1d0348
#define	V7TAR_linkname_size 100
Packit Service 1d0348
#define	V7TAR_padding_offset 257
Packit Service 1d0348
#define	V7TAR_padding_size 255
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * A filled-in copy of the header for initialization.
Packit Service 1d0348
 */
Packit Service 1d0348
static const char template_header[] = {
Packit Service 1d0348
	/* name: 100 bytes */
Packit Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,
Packit Service 1d0348
	/* Mode, space-null termination: 8 bytes */
Packit Service 1d0348
	'0','0','0','0','0','0', ' ','\0',
Packit Service 1d0348
	/* uid, space-null termination: 8 bytes */
Packit Service 1d0348
	'0','0','0','0','0','0', ' ','\0',
Packit Service 1d0348
	/* gid, space-null termination: 8 bytes */
Packit Service 1d0348
	'0','0','0','0','0','0', ' ','\0',
Packit Service 1d0348
	/* size, space termination: 12 bytes */
Packit Service 1d0348
	'0','0','0','0','0','0','0','0','0','0','0', ' ',
Packit Service 1d0348
	/* mtime, space termination: 12 bytes */
Packit Service 1d0348
	'0','0','0','0','0','0','0','0','0','0','0', ' ',
Packit Service 1d0348
	/* Initial checksum value: 8 spaces */
Packit Service 1d0348
	' ',' ',' ',' ',' ',' ',' ',' ',
Packit Service 1d0348
	/* Typeflag: 1 byte */
Packit Service 1d0348
	0,
Packit Service 1d0348
	/* Linkname: 100 bytes */
Packit Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,
Packit Service 1d0348
	/* Padding: 255 bytes */
Packit Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,0,0,0,0, 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 Service 1d0348
	0,0,0,0,0,0,0,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 Service 1d0348
};
Packit Service 1d0348
Packit Service 1d0348
static ssize_t	archive_write_v7tar_data(struct archive_write *a, const void *buff,
Packit Service 1d0348
		    size_t s);
Packit Service 1d0348
static int	archive_write_v7tar_free(struct archive_write *);
Packit Service 1d0348
static int	archive_write_v7tar_close(struct archive_write *);
Packit Service 1d0348
static int	archive_write_v7tar_finish_entry(struct archive_write *);
Packit Service 1d0348
static int	archive_write_v7tar_header(struct archive_write *,
Packit Service 1d0348
		    struct archive_entry *entry);
Packit Service 1d0348
static int	archive_write_v7tar_options(struct archive_write *,
Packit Service 1d0348
		    const char *, const char *);
Packit Service 1d0348
static int	format_256(int64_t, char *, int);
Packit Service 1d0348
static int	format_number(int64_t, char *, int size, int max, int strict);
Packit Service 1d0348
static int	format_octal(int64_t, char *, int);
Packit Service 1d0348
static int	format_header_v7tar(struct archive_write *, char h[512],
Packit Service 1d0348
		    struct archive_entry *, int, struct archive_string_conv *);
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Set output format to 'v7tar' format.
Packit Service 1d0348
 */
Packit Service 1d0348
int
Packit Service 1d0348
archive_write_set_format_v7tar(struct archive *_a)
Packit Service 1d0348
{
Packit Service 1d0348
	struct archive_write *a = (struct archive_write *)_a;
Packit Service 1d0348
	struct v7tar *v7tar;
Packit Service 1d0348
Packit Service 1d0348
	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
Packit Service 1d0348
	    ARCHIVE_STATE_NEW, "archive_write_set_format_v7tar");
Packit Service 1d0348
Packit Service 1d0348
	/* If someone else was already registered, unregister them. */
Packit Service 1d0348
	if (a->format_free != NULL)
Packit Service 1d0348
		(a->format_free)(a);
Packit Service 1d0348
Packit Service 1d0348
	/* Basic internal sanity test. */
Packit Service 1d0348
	if (sizeof(template_header) != 512) {
Packit Service 1d0348
		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
Packit Service 1d0348
		    "Internal: template_header wrong size: %zu should be 512",
Packit Service 1d0348
		    sizeof(template_header));
Packit Service 1d0348
		return (ARCHIVE_FATAL);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	v7tar = (struct v7tar *)calloc(1, sizeof(*v7tar));
Packit Service 1d0348
	if (v7tar == NULL) {
Packit Service 1d0348
		archive_set_error(&a->archive, ENOMEM,
Packit Service 1d0348
		    "Can't allocate v7tar data");
Packit Service 1d0348
		return (ARCHIVE_FATAL);
Packit Service 1d0348
	}
Packit Service 1d0348
	a->format_data = v7tar;
Packit Service 1d0348
	a->format_name = "tar (non-POSIX)";
Packit Service 1d0348
	a->format_options = archive_write_v7tar_options;
Packit Service 1d0348
	a->format_write_header = archive_write_v7tar_header;
Packit Service 1d0348
	a->format_write_data = archive_write_v7tar_data;
Packit Service 1d0348
	a->format_close = archive_write_v7tar_close;
Packit Service 1d0348
	a->format_free = archive_write_v7tar_free;
Packit Service 1d0348
	a->format_finish_entry = archive_write_v7tar_finish_entry;
Packit Service 1d0348
	a->archive.archive_format = ARCHIVE_FORMAT_TAR;
Packit Service 1d0348
	a->archive.archive_format_name = "tar (non-POSIX)";
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
archive_write_v7tar_options(struct archive_write *a, const char *key,
Packit Service 1d0348
    const char *val)
Packit Service 1d0348
{
Packit Service 1d0348
	struct v7tar *v7tar = (struct v7tar *)a->format_data;
Packit Service 1d0348
	int ret = ARCHIVE_FAILED;
Packit Service 1d0348
Packit Service 1d0348
	if (strcmp(key, "hdrcharset")  == 0) {
Packit Service 1d0348
		if (val == NULL || val[0] == 0)
Packit Service 1d0348
			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
Packit Service 1d0348
			    "%s: hdrcharset option needs a character-set name",
Packit Service 1d0348
			    a->format_name);
Packit Service 1d0348
		else {
Packit Service 1d0348
			v7tar->opt_sconv = archive_string_conversion_to_charset(
Packit Service 1d0348
			    &a->archive, val, 0);
Packit Service 1d0348
			if (v7tar->opt_sconv != NULL)
Packit Service 1d0348
				ret = ARCHIVE_OK;
Packit Service 1d0348
			else
Packit Service 1d0348
				ret = ARCHIVE_FATAL;
Packit Service 1d0348
		}
Packit Service 1d0348
		return (ret);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/* Note: The "warn" return is just to inform the options
Packit Service 1d0348
	 * supervisor that we didn't handle it.  It will generate
Packit Service 1d0348
	 * a suitable error if no one used this option. */
Packit Service 1d0348
	return (ARCHIVE_WARN);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry)
Packit Service 1d0348
{
Packit Service 1d0348
	char buff[512];
Packit Service 1d0348
	int ret, ret2;
Packit Service 1d0348
	struct v7tar *v7tar;
Packit Service 1d0348
	struct archive_entry *entry_main;
Packit Service 1d0348
	struct archive_string_conv *sconv;
Packit Service 1d0348
Packit Service 1d0348
	v7tar = (struct v7tar *)a->format_data;
Packit Service 1d0348
Packit Service 1d0348
	/* Setup default string conversion. */
Packit Service 1d0348
	if (v7tar->opt_sconv == NULL) {
Packit Service 1d0348
		if (!v7tar->init_default_conversion) {
Packit Service 1d0348
			v7tar->sconv_default =
Packit Service 1d0348
			    archive_string_default_conversion_for_write(
Packit Service 1d0348
				&(a->archive));
Packit Service 1d0348
			v7tar->init_default_conversion = 1;
Packit Service 1d0348
		}
Packit Service 1d0348
		sconv = v7tar->sconv_default;
Packit Service 1d0348
	} else
Packit Service 1d0348
		sconv = v7tar->opt_sconv;
Packit Service 1d0348
Packit Service 1d0348
	/* Sanity check. */
Packit Service 1d0348
	if (archive_entry_pathname(entry) == NULL) {
Packit Service 1d0348
		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
Packit Service 1d0348
		    "Can't record entry in tar file without pathname");
Packit Service 1d0348
		return (ARCHIVE_FAILED);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/* Only regular files (not hardlinks) have data. */
Packit Service 1d0348
	if (archive_entry_hardlink(entry) != NULL ||
Packit Service 1d0348
	    archive_entry_symlink(entry) != NULL ||
Packit Service 1d0348
	    !(archive_entry_filetype(entry) == AE_IFREG))
Packit Service 1d0348
		archive_entry_set_size(entry, 0);
Packit Service 1d0348
Packit Service 1d0348
	if (AE_IFDIR == archive_entry_filetype(entry)) {
Packit Service 1d0348
		const char *p;
Packit Service 1d0348
		size_t path_length;
Packit Service 1d0348
		/*
Packit Service 1d0348
		 * Ensure a trailing '/'.  Modify the entry so
Packit Service 1d0348
		 * the client sees the change.
Packit Service 1d0348
		 */
Packit Service 1d0348
#if defined(_WIN32) && !defined(__CYGWIN__)
Packit Service 1d0348
		const wchar_t *wp;
Packit Service 1d0348
Packit Service 1d0348
		wp = archive_entry_pathname_w(entry);
Packit Service 1d0348
		if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
Packit Service 1d0348
			struct archive_wstring ws;
Packit Service 1d0348
Packit Service 1d0348
			archive_string_init(&ws);
Packit Service 1d0348
			path_length = wcslen(wp);
Packit Service 1d0348
			if (archive_wstring_ensure(&ws,
Packit Service 1d0348
			    path_length + 2) == NULL) {
Packit Service 1d0348
				archive_set_error(&a->archive, ENOMEM,
Packit Service 1d0348
				    "Can't allocate v7tar data");
Packit Service 1d0348
				archive_wstring_free(&ws);
Packit Service 1d0348
				return(ARCHIVE_FATAL);
Packit Service 1d0348
			}
Packit Service 1d0348
			/* Should we keep '\' ? */
Packit Service 1d0348
			if (wp[path_length -1] == L'\\')
Packit Service 1d0348
				path_length--;
Packit Service 1d0348
			archive_wstrncpy(&ws, wp, path_length);
Packit Service 1d0348
			archive_wstrappend_wchar(&ws, L'/');
Packit Service 1d0348
			archive_entry_copy_pathname_w(entry, ws.s);
Packit Service 1d0348
			archive_wstring_free(&ws);
Packit Service 1d0348
			p = NULL;
Packit Service 1d0348
		} else
Packit Service 1d0348
#endif
Packit Service 1d0348
			p = archive_entry_pathname(entry);
Packit Service 1d0348
		/*
Packit Service 1d0348
		 * On Windows, this is a backup operation just in
Packit Service 1d0348
		 * case getting WCS failed. On POSIX, this is a
Packit Service 1d0348
		 * normal operation.
Packit Service 1d0348
		 */
Packit Service 1d0348
		if (p != NULL && p[strlen(p) - 1] != '/') {
Packit Service 1d0348
			struct archive_string as;
Packit Service 1d0348
Packit Service 1d0348
			archive_string_init(&as);
Packit Service 1d0348
			path_length = strlen(p);
Packit Service 1d0348
			if (archive_string_ensure(&as,
Packit Service 1d0348
			    path_length + 2) == NULL) {
Packit Service 1d0348
				archive_set_error(&a->archive, ENOMEM,
Packit Service 1d0348
				    "Can't allocate v7tar data");
Packit Service 1d0348
				archive_string_free(&as);
Packit Service 1d0348
				return(ARCHIVE_FATAL);
Packit Service 1d0348
			}
Packit Service 1d0348
#if defined(_WIN32) && !defined(__CYGWIN__)
Packit Service 1d0348
			/* NOTE: This might break the pathname
Packit Service 1d0348
			 * if the current code page is CP932 and
Packit Service 1d0348
			 * the pathname includes a character '\'
Packit Service 1d0348
			 * as a part of its multibyte pathname. */
Packit Service 1d0348
			if (p[strlen(p) -1] == '\\')
Packit Service 1d0348
				path_length--;
Packit Service 1d0348
			else
Packit Service 1d0348
#endif
Packit Service 1d0348
			archive_strncpy(&as, p, path_length);
Packit Service 1d0348
			archive_strappend_char(&as, '/');
Packit Service 1d0348
			archive_entry_copy_pathname(entry, as.s);
Packit Service 1d0348
			archive_string_free(&as);
Packit Service 1d0348
		}
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
#if defined(_WIN32) && !defined(__CYGWIN__)
Packit Service 1d0348
	/* Make sure the path separators in pathname, hardlink and symlink
Packit Service 1d0348
	 * are all slash '/', not the Windows path separator '\'. */
Packit Service 1d0348
	entry_main = __la_win_entry_in_posix_pathseparator(entry);
Packit Service 1d0348
	if (entry_main == NULL) {
Packit Service 1d0348
		archive_set_error(&a->archive, ENOMEM,
Packit Service 1d0348
		    "Can't allocate v7tar data");
Packit Service 1d0348
		return(ARCHIVE_FATAL);
Packit Service 1d0348
	}
Packit Service 1d0348
	if (entry != entry_main)
Packit Service 1d0348
		entry = entry_main;
Packit Service 1d0348
	else
Packit Service 1d0348
		entry_main = NULL;
Packit Service 1d0348
#else
Packit Service 1d0348
	entry_main = NULL;
Packit Service 1d0348
#endif
Packit Service 1d0348
	ret = format_header_v7tar(a, buff, entry, 1, sconv);
Packit Service 1d0348
	if (ret < ARCHIVE_WARN) {
Packit Service 1d0348
		if (entry_main)
Packit Service 1d0348
			archive_entry_free(entry_main);
Packit Service 1d0348
		return (ret);
Packit Service 1d0348
	}
Packit Service 1d0348
	ret2 = __archive_write_output(a, buff, 512);
Packit Service 1d0348
	if (ret2 < ARCHIVE_WARN) {
Packit Service 1d0348
		if (entry_main)
Packit Service 1d0348
			archive_entry_free(entry_main);
Packit Service 1d0348
		return (ret2);
Packit Service 1d0348
	}
Packit Service 1d0348
	if (ret2 < ret)
Packit Service 1d0348
		ret = ret2;
Packit Service 1d0348
Packit Service 1d0348
	v7tar->entry_bytes_remaining = archive_entry_size(entry);
Packit Service 1d0348
	v7tar->entry_padding = 0x1ff & (-(int64_t)v7tar->entry_bytes_remaining);
Packit Service 1d0348
	if (entry_main)
Packit Service 1d0348
		archive_entry_free(entry_main);
Packit Service 1d0348
	return (ret);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Format a basic 512-byte "v7tar" header.
Packit Service 1d0348
 *
Packit Service 1d0348
 * Returns -1 if format failed (due to field overflow).
Packit Service 1d0348
 * Note that this always formats as much of the header as possible.
Packit Service 1d0348
 * If "strict" is set to zero, it will extend numeric fields as
Packit Service 1d0348
 * necessary (overwriting terminators or using base-256 extensions).
Packit Service 1d0348
 *
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
format_header_v7tar(struct archive_write *a, char h[512],
Packit Service 1d0348
    struct archive_entry *entry, int strict,
Packit Service 1d0348
    struct archive_string_conv *sconv)
Packit Service 1d0348
{
Packit Service 1d0348
	unsigned int checksum;
Packit Service 1d0348
	int i, r, ret;
Packit Service 1d0348
	size_t copy_length;
Packit Service 1d0348
	const char *p, *pp;
Packit Service 1d0348
	int mytartype;
Packit Service 1d0348
Packit Service 1d0348
	ret = 0;
Packit Service 1d0348
	mytartype = -1;
Packit Service 1d0348
	/*
Packit Service 1d0348
	 * The "template header" already includes the "v7tar"
Packit Service 1d0348
	 * signature, various end-of-field markers and other required
Packit Service 1d0348
	 * elements.
Packit Service 1d0348
	 */
Packit Service 1d0348
	memcpy(h, &template_header, 512);
Packit Service 1d0348
Packit Service 1d0348
	/*
Packit Service 1d0348
	 * Because the block is already null-filled, and strings
Packit Service 1d0348
	 * are allowed to exactly fill their destination (without null),
Packit Service 1d0348
	 * I use memcpy(dest, src, strlen()) here a lot to copy strings.
Packit Service 1d0348
	 */
Packit Service 1d0348
	r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
Packit Service 1d0348
	if (r != 0) {
Packit Service 1d0348
		if (errno == ENOMEM) {
Packit Service 1d0348
			archive_set_error(&a->archive, ENOMEM,
Packit Service 1d0348
			    "Can't allocate memory for Pathname");
Packit Service 1d0348
			return (ARCHIVE_FATAL);
Packit Service 1d0348
		}
Packit Service 1d0348
		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
Packit Service 1d0348
		    "Can't translate pathname '%s' to %s",
Packit Service 1d0348
		    pp, archive_string_conversion_charset_name(sconv));
Packit Service 1d0348
		ret = ARCHIVE_WARN;
Packit Service 1d0348
	}
Packit Service 1d0348
	if (strict && copy_length < V7TAR_name_size)
Packit Service 1d0348
		memcpy(h + V7TAR_name_offset, pp, copy_length);
Packit Service 1d0348
	else if (!strict && copy_length <= V7TAR_name_size)
Packit Service 1d0348
		memcpy(h + V7TAR_name_offset, pp, copy_length);
Packit Service 1d0348
	else {
Packit Service 1d0348
		/* Prefix is too long. */
Packit Service 1d0348
		archive_set_error(&a->archive, ENAMETOOLONG,
Packit Service 1d0348
		    "Pathname too long");
Packit Service 1d0348
		ret = ARCHIVE_FAILED;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	r = archive_entry_hardlink_l(entry, &p, &copy_length, sconv);
Packit Service 1d0348
	if (r != 0) {
Packit Service 1d0348
		if (errno == ENOMEM) {
Packit Service 1d0348
			archive_set_error(&a->archive, ENOMEM,
Packit Service 1d0348
			    "Can't allocate memory for Linkname");
Packit Service 1d0348
			return (ARCHIVE_FATAL);
Packit Service 1d0348
		}
Packit Service 1d0348
		archive_set_error(&a->archive,
Packit Service 1d0348
		    ARCHIVE_ERRNO_FILE_FORMAT,
Packit Service 1d0348
		    "Can't translate linkname '%s' to %s",
Packit Service 1d0348
		    p, archive_string_conversion_charset_name(sconv));
Packit Service 1d0348
		ret = ARCHIVE_WARN;
Packit Service 1d0348
	}
Packit Service 1d0348
	if (copy_length > 0)
Packit Service 1d0348
		mytartype = '1';
Packit Service 1d0348
	else {
Packit Service 1d0348
		r = archive_entry_symlink_l(entry, &p, &copy_length, sconv);
Packit Service 1d0348
		if (r != 0) {
Packit Service 1d0348
			if (errno == ENOMEM) {
Packit Service 1d0348
				archive_set_error(&a->archive, ENOMEM,
Packit Service 1d0348
				    "Can't allocate memory for Linkname");
Packit Service 1d0348
				return (ARCHIVE_FATAL);
Packit Service 1d0348
			}
Packit Service 1d0348
			archive_set_error(&a->archive,
Packit Service 1d0348
			    ARCHIVE_ERRNO_FILE_FORMAT,
Packit Service 1d0348
			    "Can't translate linkname '%s' to %s",
Packit Service 1d0348
			    p, archive_string_conversion_charset_name(sconv));
Packit Service 1d0348
			ret = ARCHIVE_WARN;
Packit Service 1d0348
		}
Packit Service 1d0348
	}
Packit Service 1d0348
	if (copy_length > 0) {
Packit Service 1d0348
		if (copy_length >= V7TAR_linkname_size) {
Packit Service 1d0348
			archive_set_error(&a->archive, ENAMETOOLONG,
Packit Service 1d0348
			    "Link contents too long");
Packit Service 1d0348
			ret = ARCHIVE_FAILED;
Packit Service 1d0348
			copy_length = V7TAR_linkname_size;
Packit Service 1d0348
		}
Packit Service 1d0348
		memcpy(h + V7TAR_linkname_offset, p, copy_length);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	if (format_number(archive_entry_mode(entry) & 07777,
Packit Service 1d0348
	    h + V7TAR_mode_offset, V7TAR_mode_size,
Packit Service 1d0348
	    V7TAR_mode_max_size, strict)) {
Packit Service 1d0348
		archive_set_error(&a->archive, ERANGE,
Packit Service 1d0348
		    "Numeric mode too large");
Packit Service 1d0348
		ret = ARCHIVE_FAILED;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	if (format_number(archive_entry_uid(entry),
Packit Service 1d0348
	    h + V7TAR_uid_offset, V7TAR_uid_size, V7TAR_uid_max_size, strict)) {
Packit Service 1d0348
		archive_set_error(&a->archive, ERANGE,
Packit Service 1d0348
		    "Numeric user ID too large");
Packit Service 1d0348
		ret = ARCHIVE_FAILED;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	if (format_number(archive_entry_gid(entry),
Packit Service 1d0348
	    h + V7TAR_gid_offset, V7TAR_gid_size, V7TAR_gid_max_size, strict)) {
Packit Service 1d0348
		archive_set_error(&a->archive, ERANGE,
Packit Service 1d0348
		    "Numeric group ID too large");
Packit Service 1d0348
		ret = ARCHIVE_FAILED;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	if (format_number(archive_entry_size(entry),
Packit Service 1d0348
	    h + V7TAR_size_offset, V7TAR_size_size,
Packit Service 1d0348
	    V7TAR_size_max_size, strict)) {
Packit Service 1d0348
		archive_set_error(&a->archive, ERANGE,
Packit Service 1d0348
		    "File size out of range");
Packit Service 1d0348
		ret = ARCHIVE_FAILED;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	if (format_number(archive_entry_mtime(entry),
Packit Service 1d0348
	    h + V7TAR_mtime_offset, V7TAR_mtime_size,
Packit Service 1d0348
	    V7TAR_mtime_max_size, strict)) {
Packit Service 1d0348
		archive_set_error(&a->archive, ERANGE,
Packit Service 1d0348
		    "File modification time too large");
Packit Service 1d0348
		ret = ARCHIVE_FAILED;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	if (mytartype >= 0) {
Packit Service 1d0348
		h[V7TAR_typeflag_offset] = mytartype;
Packit Service 1d0348
	} else {
Packit Service 1d0348
		switch (archive_entry_filetype(entry)) {
Packit Service 1d0348
		case AE_IFREG: case AE_IFDIR:
Packit Service 1d0348
			break;
Packit Service 1d0348
		case AE_IFLNK:
Packit Service 1d0348
			h[V7TAR_typeflag_offset] = '2';
Packit Service 1d0348
			break;
Packit Service 1d0348
		case AE_IFCHR:
Packit Service 1d0348
			archive_set_error(&a->archive,
Packit Service 1d0348
			    ARCHIVE_ERRNO_FILE_FORMAT,
Packit Service 1d0348
			    "tar format cannot archive character device");
Packit Service 1d0348
			return (ARCHIVE_FAILED);
Packit Service 1d0348
		case AE_IFBLK:
Packit Service 1d0348
			archive_set_error(&a->archive,
Packit Service 1d0348
			    ARCHIVE_ERRNO_FILE_FORMAT,
Packit Service 1d0348
			    "tar format cannot archive block device");
Packit Service 1d0348
			return (ARCHIVE_FAILED);
Packit Service 1d0348
		case AE_IFIFO:
Packit Service 1d0348
			archive_set_error(&a->archive,
Packit Service 1d0348
			    ARCHIVE_ERRNO_FILE_FORMAT,
Packit Service 1d0348
			    "tar format cannot archive fifo");
Packit Service 1d0348
			return (ARCHIVE_FAILED);
Packit Service 1d0348
		case AE_IFSOCK:
Packit Service 1d0348
			archive_set_error(&a->archive,
Packit Service 1d0348
			    ARCHIVE_ERRNO_FILE_FORMAT,
Packit Service 1d0348
			    "tar format cannot archive socket");
Packit Service 1d0348
			return (ARCHIVE_FAILED);
Packit Service 1d0348
		default:
Packit Service 1d0348
			archive_set_error(&a->archive,
Packit Service 1d0348
			    ARCHIVE_ERRNO_FILE_FORMAT,
Packit Service 1d0348
			    "tar format cannot archive this (mode=0%lo)",
Packit Service 1d0348
			    (unsigned long)archive_entry_mode(entry));
Packit Service 1d0348
			ret = ARCHIVE_FAILED;
Packit Service 1d0348
		}
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	checksum = 0;
Packit Service 1d0348
	for (i = 0; i < 512; i++)
Packit Service 1d0348
		checksum += 255 & (unsigned int)h[i];
Packit Service 1d0348
	format_octal(checksum, h + V7TAR_checksum_offset, 6);
Packit Service 1d0348
	/* Can't be pre-set in the template. */
Packit Service 1d0348
	h[V7TAR_checksum_offset + 6] = '\0';
Packit Service 1d0348
	return (ret);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Format a number into a field, with some intelligence.
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
format_number(int64_t v, char *p, int s, int maxsize, int strict)
Packit Service 1d0348
{
Packit Service 1d0348
	int64_t limit;
Packit Service 1d0348
Packit Service 1d0348
	limit = ((int64_t)1 << (s*3));
Packit Service 1d0348
Packit Service 1d0348
	/* "Strict" only permits octal values with proper termination. */
Packit Service 1d0348
	if (strict)
Packit Service 1d0348
		return (format_octal(v, p, s));
Packit Service 1d0348
Packit Service 1d0348
	/*
Packit Service 1d0348
	 * In non-strict mode, we allow the number to overwrite one or
Packit Service 1d0348
	 * more bytes of the field termination.  Even old tar
Packit Service 1d0348
	 * implementations should be able to handle this with no
Packit Service 1d0348
	 * problem.
Packit Service 1d0348
	 */
Packit Service 1d0348
	if (v >= 0) {
Packit Service 1d0348
		while (s <= maxsize) {
Packit Service 1d0348
			if (v < limit)
Packit Service 1d0348
				return (format_octal(v, p, s));
Packit Service 1d0348
			s++;
Packit Service 1d0348
			limit <<= 3;
Packit Service 1d0348
		}
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/* Base-256 can handle any number, positive or negative. */
Packit Service 1d0348
	return (format_256(v, p, maxsize));
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Format a number into the specified field using base-256.
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
format_256(int64_t v, char *p, int s)
Packit Service 1d0348
{
Packit Service 1d0348
	p += s;
Packit Service 1d0348
	while (s-- > 0) {
Packit Service 1d0348
		*--p = (char)(v & 0xff);
Packit Service 1d0348
		v >>= 8;
Packit Service 1d0348
	}
Packit Service 1d0348
	*p |= 0x80; /* Set the base-256 marker bit. */
Packit Service 1d0348
	return (0);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Format a number into the specified field.
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
format_octal(int64_t v, char *p, int s)
Packit Service 1d0348
{
Packit Service 1d0348
	int len;
Packit Service 1d0348
Packit Service 1d0348
	len = s;
Packit Service 1d0348
Packit Service 1d0348
	/* Octal values can't be negative, so use 0. */
Packit Service 1d0348
	if (v < 0) {
Packit Service 1d0348
		while (len-- > 0)
Packit Service 1d0348
			*p++ = '0';
Packit Service 1d0348
		return (-1);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	p += s;		/* Start at the end and work backwards. */
Packit Service 1d0348
	while (s-- > 0) {
Packit Service 1d0348
		*--p = (char)('0' + (v & 7));
Packit Service 1d0348
		v >>= 3;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	if (v == 0)
Packit Service 1d0348
		return (0);
Packit Service 1d0348
Packit Service 1d0348
	/* If it overflowed, fill field with max value. */
Packit Service 1d0348
	while (len-- > 0)
Packit Service 1d0348
		*p++ = '7';
Packit Service 1d0348
Packit Service 1d0348
	return (-1);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
archive_write_v7tar_close(struct archive_write *a)
Packit Service 1d0348
{
Packit Service 1d0348
	return (__archive_write_nulls(a, 512*2));
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
archive_write_v7tar_free(struct archive_write *a)
Packit Service 1d0348
{
Packit Service 1d0348
	struct v7tar *v7tar;
Packit Service 1d0348
Packit Service 1d0348
	v7tar = (struct v7tar *)a->format_data;
Packit Service 1d0348
	free(v7tar);
Packit Service 1d0348
	a->format_data = NULL;
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
archive_write_v7tar_finish_entry(struct archive_write *a)
Packit Service 1d0348
{
Packit Service 1d0348
	struct v7tar *v7tar;
Packit Service 1d0348
	int ret;
Packit Service 1d0348
Packit Service 1d0348
	v7tar = (struct v7tar *)a->format_data;
Packit Service 1d0348
	ret = __archive_write_nulls(a,
Packit Service 1d0348
	    (size_t)(v7tar->entry_bytes_remaining + v7tar->entry_padding));
Packit Service 1d0348
	v7tar->entry_bytes_remaining = v7tar->entry_padding = 0;
Packit Service 1d0348
	return (ret);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static ssize_t
Packit Service 1d0348
archive_write_v7tar_data(struct archive_write *a, const void *buff, size_t s)
Packit Service 1d0348
{
Packit Service 1d0348
	struct v7tar *v7tar;
Packit Service 1d0348
	int ret;
Packit Service 1d0348
Packit Service 1d0348
	v7tar = (struct v7tar *)a->format_data;
Packit Service 1d0348
	if (s > v7tar->entry_bytes_remaining)
Packit Service 1d0348
		s = (size_t)v7tar->entry_bytes_remaining;
Packit Service 1d0348
	ret = __archive_write_output(a, buff, s);
Packit Service 1d0348
	v7tar->entry_bytes_remaining -= s;
Packit Service 1d0348
	if (ret != ARCHIVE_OK)
Packit Service 1d0348
		return (ret);
Packit Service 1d0348
	return (s);
Packit Service 1d0348
}