1/*- 2 * Copyright (c) 2007 Kai Wang 3 * Copyright (c) 2007 Tim Kientzle 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer 11 * in this position and unchanged. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "archive_platform.h" 29 30#ifdef HAVE_ERRNO_H 31#include <errno.h> 32#endif 33#ifdef HAVE_STDLIB_H 34#include <stdlib.h> 35#endif 36#ifdef HAVE_STRING_H 37#include <string.h> 38#endif 39 40#include "archive.h" 41#include "archive_entry.h" 42#include "archive_private.h" 43#include "archive_write_private.h" 44#include "archive_write_set_format_private.h" 45 46struct ar_w { 47 uint64_t entry_bytes_remaining; 48 uint64_t entry_padding; 49 int is_strtab; 50 int has_strtab; 51 char wrote_global_header; 52 char *strtab; 53}; 54 55/* 56 * Define structure of the "ar" header. 57 */ 58#define AR_name_offset 0 59#define AR_name_size 16 60#define AR_date_offset 16 61#define AR_date_size 12 62#define AR_uid_offset 28 63#define AR_uid_size 6 64#define AR_gid_offset 34 65#define AR_gid_size 6 66#define AR_mode_offset 40 67#define AR_mode_size 8 68#define AR_size_offset 48 69#define AR_size_size 10 70#define AR_fmag_offset 58 71#define AR_fmag_size 2 72 73static int archive_write_set_format_ar(struct archive_write *); 74static int archive_write_ar_header(struct archive_write *, 75 struct archive_entry *); 76static ssize_t archive_write_ar_data(struct archive_write *, 77 const void *buff, size_t s); 78static int archive_write_ar_free(struct archive_write *); 79static int archive_write_ar_close(struct archive_write *); 80static int archive_write_ar_finish_entry(struct archive_write *); 81static const char *ar_basename(const char *path); 82static int format_octal(int64_t v, char *p, int s); 83static int format_decimal(int64_t v, char *p, int s); 84 85int 86archive_write_set_format_ar_bsd(struct archive *_a) 87{ 88 struct archive_write *a = (struct archive_write *)_a; 89 int r; 90 91 archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, 92 ARCHIVE_STATE_NEW, "archive_write_set_format_ar_bsd"); 93 r = archive_write_set_format_ar(a); 94 if (r == ARCHIVE_OK) { 95 a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD; 96 a->archive.archive_format_name = "ar (BSD)"; 97 } 98 return (r); 99} 100 101int 102archive_write_set_format_ar_svr4(struct archive *_a) 103{ 104 struct archive_write *a = (struct archive_write *)_a; 105 int r; 106 107 archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, 108 ARCHIVE_STATE_NEW, "archive_write_set_format_ar_svr4"); 109 r = archive_write_set_format_ar(a); 110 if (r == ARCHIVE_OK) { 111 a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU; 112 a->archive.archive_format_name = "ar (GNU/SVR4)"; 113 } 114 return (r); 115} 116 117/* 118 * Generic initialization. 119 */ 120static int 121archive_write_set_format_ar(struct archive_write *a) 122{ 123 struct ar_w *ar; 124 125 /* If someone else was already registered, unregister them. */ 126 if (a->format_free != NULL) 127 (a->format_free)(a); 128 129 ar = (struct ar_w *)calloc(1, sizeof(*ar)); 130 if (ar == NULL) { 131 archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data"); 132 return (ARCHIVE_FATAL); 133 } 134 a->format_data = ar; 135 136 a->format_name = "ar"; 137 a->format_write_header = archive_write_ar_header; 138 a->format_write_data = archive_write_ar_data; 139 a->format_close = archive_write_ar_close; 140 a->format_free = archive_write_ar_free; 141 a->format_finish_entry = archive_write_ar_finish_entry; 142 return (ARCHIVE_OK); 143} 144 145static int 146archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) 147{ 148 int ret, append_fn; 149 char buff[60]; 150 char *ss, *se; 151 struct ar_w *ar; 152 const char *pathname; 153 const char *filename; 154 int64_t size; 155 156 append_fn = 0; 157 ar = (struct ar_w *)a->format_data; 158 ar->is_strtab = 0; 159 filename = NULL; 160 size = archive_entry_size(entry); 161 162 163 /* 164 * Reject files with empty name. 165 */ 166 pathname = archive_entry_pathname(entry); 167 if (pathname == NULL || *pathname == '\0') { 168 archive_set_error(&a->archive, EINVAL, 169 "Invalid filename"); 170 return (ARCHIVE_WARN); 171 } 172 173 /* 174 * If we are now at the beginning of the archive, 175 * we need first write the ar global header. 176 */ 177 if (!ar->wrote_global_header) { 178 __archive_write_output(a, "!<arch>\n", 8); 179 ar->wrote_global_header = 1; 180 } 181 182 memset(buff, ' ', 60); 183 memcpy(&buff[AR_fmag_offset], "`\n", 2); 184 185 if (strcmp(pathname, "/") == 0 ) { 186 /* Entry is archive symbol table in GNU format */ 187 buff[AR_name_offset] = '/'; 188 goto stat; 189 } 190 if (strcmp(pathname, "/SYM64/") == 0) { 191 /* Entry is archive symbol table in GNU 64-bit format */ 192 memcpy(buff + AR_name_offset, "/SYM64/", 7); 193 goto stat; 194 } 195 if (strcmp(pathname, "__.SYMDEF") == 0) { 196 /* Entry is archive symbol table in BSD format */ 197 memcpy(buff + AR_name_offset, "__.SYMDEF", 9); 198 goto stat; 199 } 200 if (strcmp(pathname, "//") == 0) { 201 /* 202 * Entry is archive filename table, inform that we should 203 * collect strtab in next _data call. 204 */ 205 ar->is_strtab = 1; 206 buff[AR_name_offset] = buff[AR_name_offset + 1] = '/'; 207 /* 208 * For archive string table, only ar_size field should 209 * be set. 210 */ 211 goto size; 212 } 213 214 /* 215 * Otherwise, entry is a normal archive member. 216 * Strip leading paths from filenames, if any. 217 */ 218 if ((filename = ar_basename(pathname)) == NULL) { 219 /* Reject filenames with trailing "/" */ 220 archive_set_error(&a->archive, EINVAL, 221 "Invalid filename"); 222 return (ARCHIVE_WARN); 223 } 224 225 if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) { 226 /* 227 * SVR4/GNU variant use a "/" to mark then end of the filename, 228 * make it possible to have embedded spaces in the filename. 229 * So, the longest filename here (without extension) is 230 * actually 15 bytes. 231 */ 232 if (strlen(filename) <= 15) { 233 memcpy(&buff[AR_name_offset], 234 filename, strlen(filename)); 235 buff[AR_name_offset + strlen(filename)] = '/'; 236 } else { 237 /* 238 * For filename longer than 15 bytes, GNU variant 239 * makes use of a string table and instead stores the 240 * offset of the real filename to in the ar_name field. 241 * The string table should have been written before. 242 */ 243 if (ar->has_strtab <= 0) { 244 archive_set_error(&a->archive, EINVAL, 245 "Can't find string table"); 246 return (ARCHIVE_WARN); 247 } 248 249 se = (char *)malloc(strlen(filename) + 3); 250 if (se == NULL) { 251 archive_set_error(&a->archive, ENOMEM, 252 "Can't allocate filename buffer"); 253 return (ARCHIVE_FATAL); 254 } 255 256 memcpy(se, filename, strlen(filename)); 257 strcpy(se + strlen(filename), "/\n"); 258 259 ss = strstr(ar->strtab, se); 260 free(se); 261 262 if (ss == NULL) { 263 archive_set_error(&a->archive, EINVAL, 264 "Invalid string table"); 265 return (ARCHIVE_WARN); 266 } 267 268 /* 269 * GNU variant puts "/" followed by digits into 270 * ar_name field. These digits indicates the real 271 * filename string's offset to the string table. 272 */ 273 buff[AR_name_offset] = '/'; 274 if (format_decimal(ss - ar->strtab, 275 buff + AR_name_offset + 1, 276 AR_name_size - 1)) { 277 archive_set_error(&a->archive, ERANGE, 278 "string table offset too large"); 279 return (ARCHIVE_WARN); 280 } 281 } 282 } else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) { 283 /* 284 * BSD variant: for any file name which is more than 285 * 16 chars or contains one or more embedded space(s), the 286 * string "#1/" followed by the ASCII length of the name is 287 * put into the ar_name field. The file size (stored in the 288 * ar_size field) is incremented by the length of the name. 289 * The name is then written immediately following the 290 * archive header. 291 */ 292 if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) { 293 memcpy(&buff[AR_name_offset], filename, strlen(filename)); 294 buff[AR_name_offset + strlen(filename)] = ' '; 295 } 296 else { 297 memcpy(buff + AR_name_offset, "#1/", 3); 298 if (format_decimal(strlen(filename), 299 buff + AR_name_offset + 3, 300 AR_name_size - 3)) { 301 archive_set_error(&a->archive, ERANGE, 302 "File name too long"); 303 return (ARCHIVE_WARN); 304 } 305 append_fn = 1; 306 size += strlen(filename); 307 } 308 } 309 310stat: 311 if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) { 312 archive_set_error(&a->archive, ERANGE, 313 "File modification time too large"); 314 return (ARCHIVE_WARN); 315 } 316 if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) { 317 archive_set_error(&a->archive, ERANGE, 318 "Numeric user ID too large"); 319 return (ARCHIVE_WARN); 320 } 321 if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) { 322 archive_set_error(&a->archive, ERANGE, 323 "Numeric group ID too large"); 324 return (ARCHIVE_WARN); 325 } 326 if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) { 327 archive_set_error(&a->archive, ERANGE, 328 "Numeric mode too large"); 329 return (ARCHIVE_WARN); 330 } 331 /* 332 * Sanity Check: A non-pseudo archive member should always be 333 * a regular file. 334 */ 335 if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) { 336 archive_set_error(&a->archive, EINVAL, 337 "Regular file required for non-pseudo member"); 338 return (ARCHIVE_WARN); 339 } 340 341size: 342 if (format_decimal(size, buff + AR_size_offset, AR_size_size)) { 343 archive_set_error(&a->archive, ERANGE, 344 "File size out of range"); 345 return (ARCHIVE_WARN); 346 } 347 348 ret = __archive_write_output(a, buff, 60); 349 if (ret != ARCHIVE_OK) 350 return (ret); 351 352 ar->entry_bytes_remaining = size; 353 ar->entry_padding = ar->entry_bytes_remaining % 2; 354 355 if (append_fn > 0) { 356 ret = __archive_write_output(a, filename, strlen(filename)); 357 if (ret != ARCHIVE_OK) 358 return (ret); 359 ar->entry_bytes_remaining -= strlen(filename); 360 } 361 362 return (ARCHIVE_OK); 363} 364 365static ssize_t 366archive_write_ar_data(struct archive_write *a, const void *buff, size_t s) 367{ 368 struct ar_w *ar; 369 int ret; 370 371 ar = (struct ar_w *)a->format_data; 372 if (s > ar->entry_bytes_remaining) 373 s = (size_t)ar->entry_bytes_remaining; 374 375 if (ar->is_strtab > 0) { 376 if (ar->has_strtab > 0) { 377 archive_set_error(&a->archive, EINVAL, 378 "More than one string tables exist"); 379 return (ARCHIVE_WARN); 380 } 381 382 ar->strtab = (char *)malloc(s + 1); 383 if (ar->strtab == NULL) { 384 archive_set_error(&a->archive, ENOMEM, 385 "Can't allocate strtab buffer"); 386 return (ARCHIVE_FATAL); 387 } 388 memcpy(ar->strtab, buff, s); 389 ar->strtab[s] = '\0'; 390 ar->has_strtab = 1; 391 } 392 393 ret = __archive_write_output(a, buff, s); 394 if (ret != ARCHIVE_OK) 395 return (ret); 396 397 ar->entry_bytes_remaining -= s; 398 return (s); 399} 400 401static int 402archive_write_ar_free(struct archive_write *a) 403{ 404 struct ar_w *ar; 405 406 ar = (struct ar_w *)a->format_data; 407 408 if (ar == NULL) 409 return (ARCHIVE_OK); 410 411 if (ar->has_strtab > 0) { 412 free(ar->strtab); 413 ar->strtab = NULL; 414 } 415 416 free(ar); 417 a->format_data = NULL; 418 return (ARCHIVE_OK); 419} 420 421static int 422archive_write_ar_close(struct archive_write *a) 423{ 424 struct ar_w *ar; 425 int ret; 426 427 /* 428 * If we haven't written anything yet, we need to write 429 * the ar global header now to make it a valid ar archive. 430 */ 431 ar = (struct ar_w *)a->format_data; 432 if (!ar->wrote_global_header) { 433 ar->wrote_global_header = 1; 434 ret = __archive_write_output(a, "!<arch>\n", 8); 435 return (ret); 436 } 437 438 return (ARCHIVE_OK); 439} 440 441static int 442archive_write_ar_finish_entry(struct archive_write *a) 443{ 444 struct ar_w *ar; 445 int ret; 446 447 ar = (struct ar_w *)a->format_data; 448 449 if (ar->entry_bytes_remaining != 0) { 450 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 451 "Entry remaining bytes larger than 0"); 452 return (ARCHIVE_WARN); 453 } 454 455 if (ar->entry_padding == 0) { 456 return (ARCHIVE_OK); 457 } 458 459 if (ar->entry_padding != 1) { 460 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 461 "Padding wrong size: %ju should be 1 or 0", 462 (uintmax_t)ar->entry_padding); 463 return (ARCHIVE_WARN); 464 } 465 466 ret = __archive_write_output(a, "\n", 1); 467 return (ret); 468} 469 470/* 471 * Format a number into the specified field using base-8. 472 * NB: This version is slightly different from the one in 473 * _ustar.c 474 */ 475static int 476format_octal(int64_t v, char *p, int s) 477{ 478 int len; 479 char *h; 480 481 len = s; 482 h = p; 483 484 /* Octal values can't be negative, so use 0. */ 485 if (v < 0) { 486 while (len-- > 0) 487 *p++ = '0'; 488 return (-1); 489 } 490 491 p += s; /* Start at the end and work backwards. */ 492 do { 493 *--p = (char)('0' + (v & 7)); 494 v >>= 3; 495 } while (--s > 0 && v > 0); 496 497 if (v == 0) { 498 memmove(h, p, len - s); 499 p = h + len - s; 500 while (s-- > 0) 501 *p++ = ' '; 502 return (0); 503 } 504 /* If it overflowed, fill field with max value. */ 505 while (len-- > 0) 506 *p++ = '7'; 507 508 return (-1); 509} 510 511/* 512 * Format a number into the specified field using base-10. 513 */ 514static int 515format_decimal(int64_t v, char *p, int s) 516{ 517 int len; 518 char *h; 519 520 len = s; 521 h = p; 522 523 /* Negative values in ar header are meaningless, so use 0. */ 524 if (v < 0) { 525 while (len-- > 0) 526 *p++ = '0'; 527 return (-1); 528 } 529 530 p += s; 531 do { 532 *--p = (char)('0' + (v % 10)); 533 v /= 10; 534 } while (--s > 0 && v > 0); 535 536 if (v == 0) { 537 memmove(h, p, len - s); 538 p = h + len - s; 539 while (s-- > 0) 540 *p++ = ' '; 541 return (0); 542 } 543 /* If it overflowed, fill field with max value. */ 544 while (len-- > 0) 545 *p++ = '9'; 546 547 return (-1); 548} 549 550static const char * 551ar_basename(const char *path) 552{ 553 const char *endp, *startp; 554 555 endp = path + strlen(path) - 1; 556 /* 557 * For filename with trailing slash(es), we return 558 * NULL indicating an error. 559 */ 560 if (*endp == '/') 561 return (NULL); 562 563 /* Find the start of the base */ 564 startp = endp; 565 while (startp > path && *(startp - 1) != '/') 566 startp--; 567 568 return (startp); 569} 570