1228753Smm/*- 2228753Smm * Copyright (c) 2007 Kai Wang 3228753Smm * Copyright (c) 2007 Tim Kientzle 4228753Smm * All rights reserved. 5228753Smm * 6228753Smm * Redistribution and use in source and binary forms, with or without 7228753Smm * modification, are permitted provided that the following conditions 8228753Smm * are met: 9228753Smm * 1. Redistributions of source code must retain the above copyright 10228753Smm * notice, this list of conditions and the following disclaimer 11228753Smm * in this position and unchanged. 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" 29229592Smm__FBSDID("$FreeBSD$"); 30228753Smm 31228753Smm#ifdef HAVE_ERRNO_H 32228753Smm#include <errno.h> 33228753Smm#endif 34228753Smm#ifdef HAVE_STDLIB_H 35228753Smm#include <stdlib.h> 36228753Smm#endif 37228753Smm#ifdef HAVE_STRING_H 38228753Smm#include <string.h> 39228753Smm#endif 40228753Smm 41228753Smm#include "archive.h" 42228753Smm#include "archive_entry.h" 43228753Smm#include "archive_private.h" 44228753Smm#include "archive_write_private.h" 45228753Smm 46228753Smmstruct ar_w { 47228753Smm uint64_t entry_bytes_remaining; 48228753Smm uint64_t entry_padding; 49228753Smm int is_strtab; 50228753Smm int has_strtab; 51228753Smm char *strtab; 52228753Smm}; 53228753Smm 54228753Smm/* 55228753Smm * Define structure of the "ar" header. 56228753Smm */ 57228753Smm#define AR_name_offset 0 58228753Smm#define AR_name_size 16 59228753Smm#define AR_date_offset 16 60228753Smm#define AR_date_size 12 61228753Smm#define AR_uid_offset 28 62228753Smm#define AR_uid_size 6 63228753Smm#define AR_gid_offset 34 64228753Smm#define AR_gid_size 6 65228753Smm#define AR_mode_offset 40 66228753Smm#define AR_mode_size 8 67228753Smm#define AR_size_offset 48 68228753Smm#define AR_size_size 10 69228753Smm#define AR_fmag_offset 58 70228753Smm#define AR_fmag_size 2 71228753Smm 72228753Smmstatic int archive_write_set_format_ar(struct archive_write *); 73228753Smmstatic int archive_write_ar_header(struct archive_write *, 74228753Smm struct archive_entry *); 75228753Smmstatic ssize_t archive_write_ar_data(struct archive_write *, 76228753Smm const void *buff, size_t s); 77228753Smmstatic int archive_write_ar_destroy(struct archive_write *); 78228753Smmstatic int archive_write_ar_finish(struct archive_write *); 79228753Smmstatic int archive_write_ar_finish_entry(struct archive_write *); 80228753Smmstatic const char *ar_basename(const char *path); 81228753Smmstatic int format_octal(int64_t v, char *p, int s); 82228753Smmstatic int format_decimal(int64_t v, char *p, int s); 83228753Smm 84228753Smmint 85228753Smmarchive_write_set_format_ar_bsd(struct archive *_a) 86228753Smm{ 87228753Smm struct archive_write *a = (struct archive_write *)_a; 88228753Smm int r = archive_write_set_format_ar(a); 89228753Smm if (r == ARCHIVE_OK) { 90228753Smm a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD; 91228753Smm a->archive.archive_format_name = "ar (BSD)"; 92228753Smm } 93228753Smm return (r); 94228753Smm} 95228753Smm 96228753Smmint 97228753Smmarchive_write_set_format_ar_svr4(struct archive *_a) 98228753Smm{ 99228753Smm struct archive_write *a = (struct archive_write *)_a; 100228753Smm int r = archive_write_set_format_ar(a); 101228753Smm if (r == ARCHIVE_OK) { 102228753Smm a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU; 103228753Smm a->archive.archive_format_name = "ar (GNU/SVR4)"; 104228753Smm } 105228753Smm return (r); 106228753Smm} 107228753Smm 108228753Smm/* 109228753Smm * Generic initialization. 110228753Smm */ 111228753Smmstatic int 112228753Smmarchive_write_set_format_ar(struct archive_write *a) 113228753Smm{ 114228753Smm struct ar_w *ar; 115228753Smm 116228753Smm /* If someone else was already registered, unregister them. */ 117228753Smm if (a->format_destroy != NULL) 118228753Smm (a->format_destroy)(a); 119228753Smm 120228753Smm ar = (struct ar_w *)malloc(sizeof(*ar)); 121228753Smm if (ar == NULL) { 122228753Smm archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data"); 123228753Smm return (ARCHIVE_FATAL); 124228753Smm } 125228753Smm memset(ar, 0, sizeof(*ar)); 126228753Smm a->format_data = ar; 127228753Smm 128228753Smm a->format_name = "ar"; 129228753Smm a->format_write_header = archive_write_ar_header; 130228753Smm a->format_write_data = archive_write_ar_data; 131228753Smm a->format_finish = archive_write_ar_finish; 132228753Smm a->format_destroy = archive_write_ar_destroy; 133228753Smm a->format_finish_entry = archive_write_ar_finish_entry; 134228753Smm return (ARCHIVE_OK); 135228753Smm} 136228753Smm 137228753Smmstatic int 138228753Smmarchive_write_ar_header(struct archive_write *a, struct archive_entry *entry) 139228753Smm{ 140228753Smm int ret, append_fn; 141228753Smm char buff[60]; 142228753Smm char *ss, *se; 143228753Smm struct ar_w *ar; 144228753Smm const char *pathname; 145228753Smm const char *filename; 146228753Smm int64_t size; 147228753Smm 148228753Smm append_fn = 0; 149228753Smm ar = (struct ar_w *)a->format_data; 150228753Smm ar->is_strtab = 0; 151228753Smm filename = NULL; 152228753Smm size = archive_entry_size(entry); 153228753Smm 154228753Smm 155228753Smm /* 156228753Smm * Reject files with empty name. 157228753Smm */ 158228753Smm pathname = archive_entry_pathname(entry); 159228753Smm if (*pathname == '\0') { 160228753Smm archive_set_error(&a->archive, EINVAL, 161228753Smm "Invalid filename"); 162228753Smm return (ARCHIVE_WARN); 163228753Smm } 164228753Smm 165228753Smm /* 166228753Smm * If we are now at the beginning of the archive, 167228753Smm * we need first write the ar global header. 168228753Smm */ 169228753Smm if (a->archive.file_position == 0) 170228753Smm (a->compressor.write)(a, "!<arch>\n", 8); 171228753Smm 172228753Smm memset(buff, ' ', 60); 173228753Smm strncpy(&buff[AR_fmag_offset], "`\n", 2); 174228753Smm 175228753Smm if (strcmp(pathname, "/") == 0 ) { 176228753Smm /* Entry is archive symbol table in GNU format */ 177228753Smm buff[AR_name_offset] = '/'; 178228753Smm goto stat; 179228753Smm } 180228753Smm if (strcmp(pathname, "__.SYMDEF") == 0) { 181228753Smm /* Entry is archive symbol table in BSD format */ 182228753Smm strncpy(buff + AR_name_offset, "__.SYMDEF", 9); 183228753Smm goto stat; 184228753Smm } 185228753Smm if (strcmp(pathname, "//") == 0) { 186228753Smm /* 187228753Smm * Entry is archive filename table, inform that we should 188228753Smm * collect strtab in next _data call. 189228753Smm */ 190228753Smm ar->is_strtab = 1; 191228753Smm buff[AR_name_offset] = buff[AR_name_offset + 1] = '/'; 192228753Smm /* 193228753Smm * For archive string table, only ar_size filed should 194228753Smm * be set. 195228753Smm */ 196228753Smm goto size; 197228753Smm } 198228753Smm 199228753Smm /* 200228753Smm * Otherwise, entry is a normal archive member. 201228753Smm * Strip leading paths from filenames, if any. 202228753Smm */ 203228753Smm if ((filename = ar_basename(pathname)) == NULL) { 204228753Smm /* Reject filenames with trailing "/" */ 205228753Smm archive_set_error(&a->archive, EINVAL, 206228753Smm "Invalid filename"); 207228753Smm return (ARCHIVE_WARN); 208228753Smm } 209228753Smm 210228753Smm if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) { 211228753Smm /* 212228753Smm * SVR4/GNU variant use a "/" to mark then end of the filename, 213228753Smm * make it possible to have embedded spaces in the filename. 214228753Smm * So, the longest filename here (without extension) is 215228753Smm * actually 15 bytes. 216228753Smm */ 217228753Smm if (strlen(filename) <= 15) { 218228753Smm strncpy(&buff[AR_name_offset], 219228753Smm filename, strlen(filename)); 220228753Smm buff[AR_name_offset + strlen(filename)] = '/'; 221228753Smm } else { 222228753Smm /* 223228753Smm * For filename longer than 15 bytes, GNU variant 224228753Smm * makes use of a string table and instead stores the 225228753Smm * offset of the real filename to in the ar_name field. 226228753Smm * The string table should have been written before. 227228753Smm */ 228228753Smm if (ar->has_strtab <= 0) { 229228753Smm archive_set_error(&a->archive, EINVAL, 230228753Smm "Can't find string table"); 231228753Smm return (ARCHIVE_WARN); 232228753Smm } 233228753Smm 234228753Smm se = (char *)malloc(strlen(filename) + 3); 235228753Smm if (se == NULL) { 236228753Smm archive_set_error(&a->archive, ENOMEM, 237228753Smm "Can't allocate filename buffer"); 238228753Smm return (ARCHIVE_FATAL); 239228753Smm } 240228753Smm 241228753Smm strncpy(se, filename, strlen(filename)); 242228753Smm strcpy(se + strlen(filename), "/\n"); 243228753Smm 244228753Smm ss = strstr(ar->strtab, se); 245228753Smm free(se); 246228753Smm 247228753Smm if (ss == NULL) { 248228753Smm archive_set_error(&a->archive, EINVAL, 249228753Smm "Invalid string table"); 250228753Smm return (ARCHIVE_WARN); 251228753Smm } 252228753Smm 253228753Smm /* 254228753Smm * GNU variant puts "/" followed by digits into 255228753Smm * ar_name field. These digits indicates the real 256228753Smm * filename string's offset to the string table. 257228753Smm */ 258228753Smm buff[AR_name_offset] = '/'; 259228753Smm if (format_decimal(ss - ar->strtab, 260228753Smm buff + AR_name_offset + 1, 261228753Smm AR_name_size - 1)) { 262228753Smm archive_set_error(&a->archive, ERANGE, 263228753Smm "string table offset too large"); 264228753Smm return (ARCHIVE_WARN); 265228753Smm } 266228753Smm } 267228753Smm } else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) { 268228753Smm /* 269228753Smm * BSD variant: for any file name which is more than 270228753Smm * 16 chars or contains one or more embedded space(s), the 271228753Smm * string "#1/" followed by the ASCII length of the name is 272228753Smm * put into the ar_name field. The file size (stored in the 273228753Smm * ar_size field) is incremented by the length of the name. 274228753Smm * The name is then written immediately following the 275228753Smm * archive header. 276228753Smm */ 277228753Smm if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) { 278228753Smm strncpy(&buff[AR_name_offset], filename, strlen(filename)); 279228753Smm buff[AR_name_offset + strlen(filename)] = ' '; 280228753Smm } 281228753Smm else { 282228753Smm strncpy(buff + AR_name_offset, "#1/", 3); 283228753Smm if (format_decimal(strlen(filename), 284228753Smm buff + AR_name_offset + 3, 285228753Smm AR_name_size - 3)) { 286228753Smm archive_set_error(&a->archive, ERANGE, 287228753Smm "File name too long"); 288228753Smm return (ARCHIVE_WARN); 289228753Smm } 290228753Smm append_fn = 1; 291228753Smm size += strlen(filename); 292228753Smm } 293228753Smm } 294228753Smm 295228753Smmstat: 296228753Smm if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) { 297228753Smm archive_set_error(&a->archive, ERANGE, 298228753Smm "File modification time too large"); 299228753Smm return (ARCHIVE_WARN); 300228753Smm } 301228753Smm if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) { 302228753Smm archive_set_error(&a->archive, ERANGE, 303228753Smm "Numeric user ID too large"); 304228753Smm return (ARCHIVE_WARN); 305228753Smm } 306228753Smm if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) { 307228753Smm archive_set_error(&a->archive, ERANGE, 308228753Smm "Numeric group ID too large"); 309228753Smm return (ARCHIVE_WARN); 310228753Smm } 311228753Smm if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) { 312228753Smm archive_set_error(&a->archive, ERANGE, 313228753Smm "Numeric mode too large"); 314228753Smm return (ARCHIVE_WARN); 315228753Smm } 316228753Smm /* 317228753Smm * Sanity Check: A non-pseudo archive member should always be 318228753Smm * a regular file. 319228753Smm */ 320228753Smm if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) { 321228753Smm archive_set_error(&a->archive, EINVAL, 322228753Smm "Regular file required for non-pseudo member"); 323228753Smm return (ARCHIVE_WARN); 324228753Smm } 325228753Smm 326228753Smmsize: 327228753Smm if (format_decimal(size, buff + AR_size_offset, AR_size_size)) { 328228753Smm archive_set_error(&a->archive, ERANGE, 329228753Smm "File size out of range"); 330228753Smm return (ARCHIVE_WARN); 331228753Smm } 332228753Smm 333228753Smm ret = (a->compressor.write)(a, buff, 60); 334228753Smm if (ret != ARCHIVE_OK) 335228753Smm return (ret); 336228753Smm 337228753Smm ar->entry_bytes_remaining = size; 338228753Smm ar->entry_padding = ar->entry_bytes_remaining % 2; 339228753Smm 340228753Smm if (append_fn > 0) { 341228753Smm ret = (a->compressor.write)(a, filename, strlen(filename)); 342228753Smm if (ret != ARCHIVE_OK) 343228753Smm return (ret); 344228753Smm ar->entry_bytes_remaining -= strlen(filename); 345228753Smm } 346228753Smm 347228753Smm return (ARCHIVE_OK); 348228753Smm} 349228753Smm 350228753Smmstatic ssize_t 351228753Smmarchive_write_ar_data(struct archive_write *a, const void *buff, size_t s) 352228753Smm{ 353228753Smm struct ar_w *ar; 354228753Smm int ret; 355228753Smm 356228753Smm ar = (struct ar_w *)a->format_data; 357228753Smm if (s > ar->entry_bytes_remaining) 358228753Smm s = ar->entry_bytes_remaining; 359228753Smm 360228753Smm if (ar->is_strtab > 0) { 361228753Smm if (ar->has_strtab > 0) { 362228753Smm archive_set_error(&a->archive, EINVAL, 363228753Smm "More than one string tables exist"); 364228753Smm return (ARCHIVE_WARN); 365228753Smm } 366228753Smm 367228753Smm ar->strtab = (char *)malloc(s); 368228753Smm if (ar->strtab == NULL) { 369228753Smm archive_set_error(&a->archive, ENOMEM, 370228753Smm "Can't allocate strtab buffer"); 371228753Smm return (ARCHIVE_FATAL); 372228753Smm } 373228753Smm strncpy(ar->strtab, buff, s); 374228753Smm ar->has_strtab = 1; 375228753Smm } 376228753Smm 377228753Smm ret = (a->compressor.write)(a, buff, s); 378228753Smm if (ret != ARCHIVE_OK) 379228753Smm return (ret); 380228753Smm 381228753Smm ar->entry_bytes_remaining -= s; 382228753Smm return (s); 383228753Smm} 384228753Smm 385228753Smmstatic int 386228753Smmarchive_write_ar_destroy(struct archive_write *a) 387228753Smm{ 388228753Smm struct ar_w *ar; 389228753Smm 390228753Smm ar = (struct ar_w *)a->format_data; 391228753Smm 392228753Smm if (ar == NULL) 393228753Smm return (ARCHIVE_OK); 394228753Smm 395228753Smm if (ar->has_strtab > 0) { 396228753Smm free(ar->strtab); 397228753Smm ar->strtab = NULL; 398228753Smm } 399228753Smm 400228753Smm free(ar); 401228753Smm a->format_data = NULL; 402228753Smm return (ARCHIVE_OK); 403228753Smm} 404228753Smm 405228753Smmstatic int 406228753Smmarchive_write_ar_finish(struct archive_write *a) 407228753Smm{ 408228753Smm int ret; 409228753Smm 410228753Smm /* 411228753Smm * If we haven't written anything yet, we need to write 412228753Smm * the ar global header now to make it a valid ar archive. 413228753Smm */ 414228753Smm if (a->archive.file_position == 0) { 415228753Smm ret = (a->compressor.write)(a, "!<arch>\n", 8); 416228753Smm return (ret); 417228753Smm } 418228753Smm 419228753Smm return (ARCHIVE_OK); 420228753Smm} 421228753Smm 422228753Smmstatic int 423228753Smmarchive_write_ar_finish_entry(struct archive_write *a) 424228753Smm{ 425228753Smm struct ar_w *ar; 426228753Smm int ret; 427228753Smm 428228753Smm ar = (struct ar_w *)a->format_data; 429228753Smm 430228753Smm if (ar->entry_bytes_remaining != 0) { 431228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 432228753Smm "Entry remaining bytes larger than 0"); 433228753Smm return (ARCHIVE_WARN); 434228753Smm } 435228753Smm 436228753Smm if (ar->entry_padding == 0) { 437228753Smm return (ARCHIVE_OK); 438228753Smm } 439228753Smm 440228753Smm if (ar->entry_padding != 1) { 441228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 442228753Smm "Padding wrong size: %d should be 1 or 0", 443228753Smm (int)ar->entry_padding); 444228753Smm return (ARCHIVE_WARN); 445228753Smm } 446228753Smm 447228753Smm ret = (a->compressor.write)(a, "\n", 1); 448228753Smm return (ret); 449228753Smm} 450228753Smm 451228753Smm/* 452228753Smm * Format a number into the specified field using base-8. 453228753Smm * NB: This version is slightly different from the one in 454228753Smm * _ustar.c 455228753Smm */ 456228753Smmstatic int 457228753Smmformat_octal(int64_t v, char *p, int s) 458228753Smm{ 459228753Smm int len; 460228753Smm char *h; 461228753Smm 462228753Smm len = s; 463228753Smm h = p; 464228753Smm 465228753Smm /* Octal values can't be negative, so use 0. */ 466228753Smm if (v < 0) { 467228753Smm while (len-- > 0) 468228753Smm *p++ = '0'; 469228753Smm return (-1); 470228753Smm } 471228753Smm 472228753Smm p += s; /* Start at the end and work backwards. */ 473228753Smm do { 474228753Smm *--p = (char)('0' + (v & 7)); 475228753Smm v >>= 3; 476228753Smm } while (--s > 0 && v > 0); 477228753Smm 478228753Smm if (v == 0) { 479228753Smm memmove(h, p, len - s); 480228753Smm p = h + len - s; 481228753Smm while (s-- > 0) 482228753Smm *p++ = ' '; 483228753Smm return (0); 484228753Smm } 485228753Smm /* If it overflowed, fill field with max value. */ 486228753Smm while (len-- > 0) 487228753Smm *p++ = '7'; 488228753Smm 489228753Smm return (-1); 490228753Smm} 491228753Smm 492228753Smm/* 493228753Smm * Format a number into the specified field using base-10. 494228753Smm */ 495228753Smmstatic int 496228753Smmformat_decimal(int64_t v, char *p, int s) 497228753Smm{ 498228753Smm int len; 499228753Smm char *h; 500228753Smm 501228753Smm len = s; 502228753Smm h = p; 503228753Smm 504228753Smm /* Negative values in ar header are meaningless , so use 0. */ 505228753Smm if (v < 0) { 506228753Smm while (len-- > 0) 507228753Smm *p++ = '0'; 508228753Smm return (-1); 509228753Smm } 510228753Smm 511228753Smm p += s; 512228753Smm do { 513228753Smm *--p = (char)('0' + (v % 10)); 514228753Smm v /= 10; 515228753Smm } while (--s > 0 && v > 0); 516228753Smm 517228753Smm if (v == 0) { 518228753Smm memmove(h, p, len - s); 519228753Smm p = h + len - s; 520228753Smm while (s-- > 0) 521228753Smm *p++ = ' '; 522228753Smm return (0); 523228753Smm } 524228753Smm /* If it overflowed, fill field with max value. */ 525228753Smm while (len-- > 0) 526228753Smm *p++ = '9'; 527228753Smm 528228753Smm return (-1); 529228753Smm} 530228753Smm 531228753Smmstatic const char * 532228753Smmar_basename(const char *path) 533228753Smm{ 534228753Smm const char *endp, *startp; 535228753Smm 536228753Smm endp = path + strlen(path) - 1; 537228753Smm /* 538228753Smm * For filename with trailing slash(es), we return 539228753Smm * NULL indicating an error. 540228753Smm */ 541228753Smm if (*endp == '/') 542228753Smm return (NULL); 543228753Smm 544228753Smm /* Find the start of the base */ 545228753Smm startp = endp; 546228753Smm while (startp > path && *(startp - 1) != '/') 547228753Smm startp--; 548228753Smm 549228753Smm return (startp); 550228753Smm} 551