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