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