1228753Smm/*- 2228753Smm * Copyright (c) 2003-2007 Tim Kientzle 3228753Smm * Copyright (c) 2006 Rudolf Marek SYSGO s.r.o. 4232153Smm * Copyright (c) 2011-2012 Michihiro NAKAJIMA 5228753Smm * All rights reserved. 6228753Smm * 7228753Smm * Redistribution and use in source and binary forms, with or without 8228753Smm * modification, are permitted provided that the following conditions 9228753Smm * are met: 10228753Smm * 1. Redistributions of source code must retain the above copyright 11228753Smm * notice, this list of conditions and the following disclaimer. 12228753Smm * 2. Redistributions in binary form must reproduce the above copyright 13228753Smm * notice, this list of conditions and the following disclaimer in the 14228753Smm * documentation and/or other materials provided with the distribution. 15228753Smm * 16228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 17228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 20228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26228753Smm */ 27228753Smm 28228753Smm#include "archive_platform.h" 29228763Smm__FBSDID("$FreeBSD$"); 30228753Smm 31228753Smm#ifdef HAVE_ERRNO_H 32228753Smm#include <errno.h> 33228753Smm#endif 34228753Smm#include <stdio.h> 35228753Smm#ifdef HAVE_STDLIB_H 36228753Smm#include <stdlib.h> 37228753Smm#endif 38228753Smm#ifdef HAVE_STRING_H 39228753Smm#include <string.h> 40228753Smm#endif 41228753Smm 42228753Smm#include "archive.h" 43228753Smm#include "archive_entry.h" 44232153Smm#include "archive_entry_locale.h" 45228753Smm#include "archive_private.h" 46228753Smm#include "archive_write_private.h" 47228753Smm 48228753Smmstatic ssize_t archive_write_newc_data(struct archive_write *, 49228753Smm const void *buff, size_t s); 50232153Smmstatic int archive_write_newc_close(struct archive_write *); 51232153Smmstatic int archive_write_newc_free(struct archive_write *); 52228753Smmstatic int archive_write_newc_finish_entry(struct archive_write *); 53228753Smmstatic int archive_write_newc_header(struct archive_write *, 54228753Smm struct archive_entry *); 55232153Smmstatic int archive_write_newc_options(struct archive_write *, 56232153Smm const char *, const char *); 57228753Smmstatic int format_hex(int64_t, void *, int); 58228753Smmstatic int64_t format_hex_recursive(int64_t, char *, int); 59232153Smmstatic int write_header(struct archive_write *, struct archive_entry *); 60228753Smm 61228753Smmstruct cpio { 62228753Smm uint64_t entry_bytes_remaining; 63228753Smm int padding; 64228753Smm 65232153Smm struct archive_string_conv *opt_sconv; 66232153Smm struct archive_string_conv *sconv_default; 67232153Smm int init_default_conversion; 68228753Smm}; 69228753Smm 70232153Smm#define c_magic_offset 0 71232153Smm#define c_magic_size 6 72232153Smm#define c_ino_offset 6 73232153Smm#define c_ino_size 8 74232153Smm#define c_mode_offset 14 75232153Smm#define c_mode_size 8 76232153Smm#define c_uid_offset 22 77232153Smm#define c_uid_size 8 78232153Smm#define c_gid_offset 30 79232153Smm#define c_gid_size 8 80232153Smm#define c_nlink_offset 38 81232153Smm#define c_nlink_size 8 82232153Smm#define c_mtime_offset 46 83232153Smm#define c_mtime_size 8 84232153Smm#define c_filesize_offset 54 85232153Smm#define c_filesize_size 8 86232153Smm#define c_devmajor_offset 62 87232153Smm#define c_devmajor_size 8 88232153Smm#define c_devminor_offset 70 89232153Smm#define c_devminor_size 8 90232153Smm#define c_rdevmajor_offset 78 91232153Smm#define c_rdevmajor_size 8 92232153Smm#define c_rdevminor_offset 86 93232153Smm#define c_rdevminor_size 8 94232153Smm#define c_namesize_offset 94 95232153Smm#define c_namesize_size 8 96232153Smm#define c_checksum_offset 102 97232153Smm#define c_checksum_size 8 98232153Smm#define c_header_size 110 99232153Smm 100228753Smm/* Logic trick: difference between 'n' and next multiple of 4 */ 101228753Smm#define PAD4(n) (3 & (1 + ~(n))) 102228753Smm 103228753Smm/* 104228753Smm * Set output format to 'cpio' format. 105228753Smm */ 106228753Smmint 107228753Smmarchive_write_set_format_cpio_newc(struct archive *_a) 108228753Smm{ 109228753Smm struct archive_write *a = (struct archive_write *)_a; 110228753Smm struct cpio *cpio; 111228753Smm 112232153Smm archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, 113232153Smm ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_newc"); 114232153Smm 115228753Smm /* If someone else was already registered, unregister them. */ 116232153Smm if (a->format_free != NULL) 117232153Smm (a->format_free)(a); 118228753Smm 119228753Smm cpio = (struct cpio *)malloc(sizeof(*cpio)); 120228753Smm if (cpio == NULL) { 121228753Smm archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); 122228753Smm return (ARCHIVE_FATAL); 123228753Smm } 124228753Smm memset(cpio, 0, sizeof(*cpio)); 125228753Smm a->format_data = cpio; 126228753Smm a->format_name = "cpio"; 127232153Smm a->format_options = archive_write_newc_options; 128228753Smm a->format_write_header = archive_write_newc_header; 129228753Smm a->format_write_data = archive_write_newc_data; 130228753Smm a->format_finish_entry = archive_write_newc_finish_entry; 131232153Smm a->format_close = archive_write_newc_close; 132232153Smm a->format_free = archive_write_newc_free; 133228753Smm a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC; 134228753Smm a->archive.archive_format_name = "SVR4 cpio nocrc"; 135228753Smm return (ARCHIVE_OK); 136228753Smm} 137228753Smm 138228753Smmstatic int 139232153Smmarchive_write_newc_options(struct archive_write *a, const char *key, 140232153Smm const char *val) 141232153Smm{ 142232153Smm struct cpio *cpio = (struct cpio *)a->format_data; 143232153Smm int ret = ARCHIVE_FAILED; 144232153Smm 145232153Smm if (strcmp(key, "hdrcharset") == 0) { 146232153Smm if (val == NULL || val[0] == 0) 147232153Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 148232153Smm "%s: hdrcharset option needs a character-set name", 149232153Smm a->format_name); 150232153Smm else { 151232153Smm cpio->opt_sconv = archive_string_conversion_to_charset( 152232153Smm &a->archive, val, 0); 153232153Smm if (cpio->opt_sconv != NULL) 154232153Smm ret = ARCHIVE_OK; 155232153Smm else 156232153Smm ret = ARCHIVE_FATAL; 157232153Smm } 158232153Smm return (ret); 159232153Smm } 160232153Smm 161232153Smm /* Note: The "warn" return is just to inform the options 162232153Smm * supervisor that we didn't handle it. It will generate 163232153Smm * a suitable error if no one used this option. */ 164232153Smm return (ARCHIVE_WARN); 165232153Smm} 166232153Smm 167232153Smmstatic struct archive_string_conv * 168232153Smmget_sconv(struct archive_write *a) 169232153Smm{ 170232153Smm struct cpio *cpio; 171232153Smm struct archive_string_conv *sconv; 172232153Smm 173232153Smm cpio = (struct cpio *)a->format_data; 174232153Smm sconv = cpio->opt_sconv; 175232153Smm if (sconv == NULL) { 176232153Smm if (!cpio->init_default_conversion) { 177232153Smm cpio->sconv_default = 178232153Smm archive_string_default_conversion_for_write( 179232153Smm &(a->archive)); 180232153Smm cpio->init_default_conversion = 1; 181232153Smm } 182232153Smm sconv = cpio->sconv_default; 183232153Smm } 184232153Smm return (sconv); 185232153Smm} 186232153Smm 187232153Smmstatic int 188228753Smmarchive_write_newc_header(struct archive_write *a, struct archive_entry *entry) 189228753Smm{ 190232153Smm const char *path; 191232153Smm size_t len; 192232153Smm 193232153Smm if (archive_entry_filetype(entry) == 0) { 194232153Smm archive_set_error(&a->archive, -1, "Filetype required"); 195232153Smm return (ARCHIVE_FAILED); 196232153Smm } 197232153Smm 198232153Smm if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0 199232153Smm && errno == ENOMEM) { 200232153Smm archive_set_error(&a->archive, ENOMEM, 201232153Smm "Can't allocate memory for Pathname"); 202232153Smm return (ARCHIVE_FATAL); 203232153Smm } 204232153Smm if (len == 0 || path == NULL || path[0] == '\0') { 205232153Smm archive_set_error(&a->archive, -1, "Pathname required"); 206232153Smm return (ARCHIVE_FAILED); 207232153Smm } 208232153Smm 209232153Smm if (archive_entry_hardlink(entry) == NULL 210232153Smm && (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0)) { 211232153Smm archive_set_error(&a->archive, -1, "Size required"); 212232153Smm return (ARCHIVE_FAILED); 213232153Smm } 214232153Smm return write_header(a, entry); 215232153Smm} 216232153Smm 217232153Smmstatic int 218232153Smmwrite_header(struct archive_write *a, struct archive_entry *entry) 219232153Smm{ 220228753Smm int64_t ino; 221228753Smm struct cpio *cpio; 222228753Smm const char *p, *path; 223232153Smm int pathlength, ret, ret_final; 224232153Smm char h[c_header_size]; 225232153Smm struct archive_string_conv *sconv; 226232153Smm struct archive_entry *entry_main; 227232153Smm size_t len; 228228753Smm int pad; 229228753Smm 230228753Smm cpio = (struct cpio *)a->format_data; 231232153Smm ret_final = ARCHIVE_OK; 232232153Smm sconv = get_sconv(a); 233228753Smm 234232153Smm#if defined(_WIN32) && !defined(__CYGWIN__) 235232153Smm /* Make sure the path separators in pahtname, hardlink and symlink 236232153Smm * are all slash '/', not the Windows path separator '\'. */ 237232153Smm entry_main = __la_win_entry_in_posix_pathseparator(entry); 238232153Smm if (entry_main == NULL) { 239232153Smm archive_set_error(&a->archive, ENOMEM, 240232153Smm "Can't allocate ustar data"); 241232153Smm return(ARCHIVE_FATAL); 242232153Smm } 243232153Smm if (entry != entry_main) 244232153Smm entry = entry_main; 245232153Smm else 246232153Smm entry_main = NULL; 247232153Smm#else 248232153Smm entry_main = NULL; 249232153Smm#endif 250228753Smm 251232153Smm ret = archive_entry_pathname_l(entry, &path, &len, sconv); 252232153Smm if (ret != 0) { 253232153Smm if (errno == ENOMEM) { 254232153Smm archive_set_error(&a->archive, ENOMEM, 255232153Smm "Can't allocate memory for Pathname"); 256232153Smm ret_final = ARCHIVE_FATAL; 257232153Smm goto exit_write_header; 258232153Smm } 259232153Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, 260232153Smm "Can't translate pathname '%s' to %s", 261232153Smm archive_entry_pathname(entry), 262232153Smm archive_string_conversion_charset_name(sconv)); 263232153Smm ret_final = ARCHIVE_WARN; 264232153Smm } 265232153Smm pathlength = (int)len + 1; /* Include trailing null. */ 266228753Smm 267232153Smm memset(h, 0, c_header_size); 268232153Smm format_hex(0x070701, h + c_magic_offset, c_magic_size); 269232153Smm format_hex(archive_entry_devmajor(entry), h + c_devmajor_offset, 270232153Smm c_devmajor_size); 271232153Smm format_hex(archive_entry_devminor(entry), h + c_devminor_offset, 272232153Smm c_devminor_size); 273232153Smm 274228753Smm ino = archive_entry_ino64(entry); 275228753Smm if (ino > 0xffffffff) { 276228753Smm archive_set_error(&a->archive, ERANGE, 277228753Smm "large inode number truncated"); 278232153Smm ret_final = ARCHIVE_WARN; 279228753Smm } 280228753Smm 281232153Smm /* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */ 282232153Smm format_hex(ino & 0xffffffff, h + c_ino_offset, c_ino_size); 283232153Smm format_hex(archive_entry_mode(entry), h + c_mode_offset, c_mode_size); 284232153Smm format_hex(archive_entry_uid(entry), h + c_uid_offset, c_uid_size); 285232153Smm format_hex(archive_entry_gid(entry), h + c_gid_offset, c_gid_size); 286232153Smm format_hex(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size); 287228753Smm if (archive_entry_filetype(entry) == AE_IFBLK 288228753Smm || archive_entry_filetype(entry) == AE_IFCHR) { 289232153Smm format_hex(archive_entry_rdevmajor(entry), h + c_rdevmajor_offset, c_rdevmajor_size); 290232153Smm format_hex(archive_entry_rdevminor(entry), h + c_rdevminor_offset, c_rdevminor_size); 291228753Smm } else { 292232153Smm format_hex(0, h + c_rdevmajor_offset, c_rdevmajor_size); 293232153Smm format_hex(0, h + c_rdevminor_offset, c_rdevminor_size); 294228753Smm } 295232153Smm format_hex(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size); 296232153Smm format_hex(pathlength, h + c_namesize_offset, c_namesize_size); 297232153Smm format_hex(0, h + c_checksum_offset, c_checksum_size); 298228753Smm 299228753Smm /* Non-regular files don't store bodies. */ 300228753Smm if (archive_entry_filetype(entry) != AE_IFREG) 301228753Smm archive_entry_set_size(entry, 0); 302228753Smm 303228753Smm /* Symlinks get the link written as the body of the entry. */ 304232153Smm ret = archive_entry_symlink_l(entry, &p, &len, sconv); 305232153Smm if (ret != 0) { 306232153Smm if (errno == ENOMEM) { 307232153Smm archive_set_error(&a->archive, ENOMEM, 308232153Smm "Can't allocate memory for Likname"); 309232153Smm ret_final = ARCHIVE_FATAL; 310232153Smm goto exit_write_header; 311232153Smm } 312232153Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, 313232153Smm "Can't translate linkname '%s' to %s", 314232153Smm archive_entry_symlink(entry), 315232153Smm archive_string_conversion_charset_name(sconv)); 316232153Smm ret_final = ARCHIVE_WARN; 317232153Smm } 318232153Smm if (len > 0 && p != NULL && *p != '\0') 319232153Smm ret = format_hex(strlen(p), h + c_filesize_offset, 320232153Smm c_filesize_size); 321228753Smm else 322232153Smm ret = format_hex(archive_entry_size(entry), 323232153Smm h + c_filesize_offset, c_filesize_size); 324232153Smm if (ret) { 325232153Smm archive_set_error(&a->archive, ERANGE, 326232153Smm "File is too large for this format."); 327232153Smm ret_final = ARCHIVE_FAILED; 328232153Smm goto exit_write_header; 329232153Smm } 330228753Smm 331232153Smm ret = __archive_write_output(a, h, c_header_size); 332232153Smm if (ret != ARCHIVE_OK) { 333232153Smm ret_final = ARCHIVE_FATAL; 334232153Smm goto exit_write_header; 335232153Smm } 336228753Smm 337228753Smm /* Pad pathname to even length. */ 338232153Smm ret = __archive_write_output(a, path, pathlength); 339232153Smm if (ret != ARCHIVE_OK) { 340232153Smm ret_final = ARCHIVE_FATAL; 341232153Smm goto exit_write_header; 342232153Smm } 343232153Smm pad = PAD4(pathlength + c_header_size); 344232153Smm if (pad) { 345232153Smm ret = __archive_write_output(a, "\0\0\0", pad); 346232153Smm if (ret != ARCHIVE_OK) { 347232153Smm ret_final = ARCHIVE_FATAL; 348232153Smm goto exit_write_header; 349232153Smm } 350232153Smm } 351228753Smm 352228753Smm cpio->entry_bytes_remaining = archive_entry_size(entry); 353238856Smm cpio->padding = (int)PAD4(cpio->entry_bytes_remaining); 354228753Smm 355228753Smm /* Write the symlink now. */ 356228753Smm if (p != NULL && *p != '\0') { 357232153Smm ret = __archive_write_output(a, p, strlen(p)); 358232153Smm if (ret != ARCHIVE_OK) { 359232153Smm ret_final = ARCHIVE_FATAL; 360232153Smm goto exit_write_header; 361232153Smm } 362228753Smm pad = PAD4(strlen(p)); 363232153Smm ret = __archive_write_output(a, "\0\0\0", pad); 364232153Smm if (ret != ARCHIVE_OK) { 365232153Smm ret_final = ARCHIVE_FATAL; 366232153Smm goto exit_write_header; 367232153Smm } 368228753Smm } 369232153Smmexit_write_header: 370232153Smm if (entry_main) 371232153Smm archive_entry_free(entry_main); 372232153Smm return (ret_final); 373228753Smm} 374228753Smm 375228753Smmstatic ssize_t 376228753Smmarchive_write_newc_data(struct archive_write *a, const void *buff, size_t s) 377228753Smm{ 378228753Smm struct cpio *cpio; 379228753Smm int ret; 380228753Smm 381228753Smm cpio = (struct cpio *)a->format_data; 382228753Smm if (s > cpio->entry_bytes_remaining) 383238856Smm s = (size_t)cpio->entry_bytes_remaining; 384228753Smm 385232153Smm ret = __archive_write_output(a, buff, s); 386228753Smm cpio->entry_bytes_remaining -= s; 387228753Smm if (ret >= 0) 388228753Smm return (s); 389228753Smm else 390228753Smm return (ret); 391228753Smm} 392228753Smm 393228753Smm/* 394228753Smm * Format a number into the specified field. 395228753Smm */ 396228753Smmstatic int 397228753Smmformat_hex(int64_t v, void *p, int digits) 398228753Smm{ 399228753Smm int64_t max; 400228753Smm int ret; 401228753Smm 402228753Smm max = (((int64_t)1) << (digits * 4)) - 1; 403228753Smm if (v >= 0 && v <= max) { 404228753Smm format_hex_recursive(v, (char *)p, digits); 405228753Smm ret = 0; 406228753Smm } else { 407228753Smm format_hex_recursive(max, (char *)p, digits); 408228753Smm ret = -1; 409228753Smm } 410228753Smm return (ret); 411228753Smm} 412228753Smm 413228753Smmstatic int64_t 414228753Smmformat_hex_recursive(int64_t v, char *p, int s) 415228753Smm{ 416228753Smm if (s == 0) 417228753Smm return (v); 418228753Smm v = format_hex_recursive(v, p+1, s-1); 419228753Smm *p = "0123456789abcdef"[v & 0xf]; 420228753Smm return (v >> 4); 421228753Smm} 422228753Smm 423228753Smmstatic int 424232153Smmarchive_write_newc_close(struct archive_write *a) 425228753Smm{ 426228753Smm int er; 427228753Smm struct archive_entry *trailer; 428228753Smm 429228753Smm trailer = archive_entry_new(); 430228753Smm archive_entry_set_nlink(trailer, 1); 431232153Smm archive_entry_set_size(trailer, 0); 432228753Smm archive_entry_set_pathname(trailer, "TRAILER!!!"); 433232153Smm /* Bypass the required data checks. */ 434232153Smm er = write_header(a, trailer); 435228753Smm archive_entry_free(trailer); 436228753Smm return (er); 437228753Smm} 438228753Smm 439228753Smmstatic int 440232153Smmarchive_write_newc_free(struct archive_write *a) 441228753Smm{ 442228753Smm struct cpio *cpio; 443228753Smm 444228753Smm cpio = (struct cpio *)a->format_data; 445228753Smm free(cpio); 446228753Smm a->format_data = NULL; 447228753Smm return (ARCHIVE_OK); 448228753Smm} 449228753Smm 450228753Smmstatic int 451228753Smmarchive_write_newc_finish_entry(struct archive_write *a) 452228753Smm{ 453228753Smm struct cpio *cpio; 454228753Smm 455228753Smm cpio = (struct cpio *)a->format_data; 456238856Smm return (__archive_write_nulls(a, 457238856Smm (size_t)cpio->entry_bytes_remaining + cpio->padding)); 458228753Smm} 459