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