1 %{ 2 /*- 3 * Copyright (c) 2008 Kai Wang 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 <sys/mman.h> 29 #include <sys/param.h> 30 #include <sys/queue.h> 31 #include <sys/stat.h> 32 33 #include <archive.h> 34 #include <archive_entry.h> 35 #include <dirent.h> 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include "libelftc.h" 44 45 #include "ar.h" 46 47 ELFTC_VCSID("$Id"); 48 49 50 #define TEMPLATE "arscp.XXXXXXXX" 51 52 struct list { 53 char *str; 54 struct list *next; 55 }; 56 57 58 extern int yylex(void); 59 extern int yyparse(void); 60 61 static void yyerror(const char *); 62 static void arscp_addlib(char *archive, struct list *list); 63 static void arscp_addmod(struct list *list); 64 static void arscp_clear(void); 65 static void arscp_create(char *in, char *out); 66 static void arscp_delete(struct list *list); 67 static void arscp_dir(char *archive, struct list *list, char *rlt); 68 static void arscp_end(int eval); 69 static void arscp_extract(struct list *list); 70 static void arscp_free_argv(void); 71 static void arscp_free_mlist(struct list *list); 72 static void arscp_list(void); 73 static struct list *arscp_mlist(struct list *list, char *str); 74 static void arscp_mlist2argv(struct list *list); 75 static int arscp_mlist_len(struct list *list); 76 static void arscp_open(char *fname); 77 static void arscp_prompt(void); 78 static void arscp_replace(struct list *list); 79 static void arscp_save(void); 80 static int arscp_target_exist(void); 81 82 extern int lineno; 83 84 static struct bsdar *bsdar; 85 static char *target; 86 static char *tmpac; 87 static int interactive; 88 static int verbose; 89 90 %} 91 92 %token ADDLIB 93 %token ADDMOD 94 %token CLEAR 95 %token CREATE 96 %token DELETE 97 %token DIRECTORY 98 %token END 99 %token EXTRACT 100 %token LIST 101 %token OPEN 102 %token REPLACE 103 %token VERBOSE 104 %token SAVE 105 %token LP 106 %token RP 107 %token COMMA 108 %token EOL 109 %token <str> FNAME 110 %type <list> mod_list 111 112 %union { 113 char *str; 114 struct list *list; 115 } 116 117 %% 118 119 begin 120 : { arscp_prompt(); } ar_script 121 ; 122 123 ar_script 124 : cmd_list 125 | 126 ; 127 128 mod_list 129 : FNAME { $$ = arscp_mlist(NULL, $1); } 130 | mod_list separator FNAME { $$ = arscp_mlist($1, $3); } 131 ; 132 133 separator 134 : COMMA 135 | 136 ; 137 138 cmd_list 139 : rawcmd 140 | cmd_list rawcmd 141 ; 142 143 rawcmd 144 : cmd EOL { arscp_prompt(); } 145 ; 146 147 cmd 148 : addlib_cmd 149 | addmod_cmd 150 | clear_cmd 151 | create_cmd 152 | delete_cmd 153 | directory_cmd 154 | end_cmd 155 | extract_cmd 156 | list_cmd 157 | open_cmd 158 | replace_cmd 159 | verbose_cmd 160 | save_cmd 161 | invalid_cmd 162 | empty_cmd 163 | error 164 ; 165 166 addlib_cmd 167 : ADDLIB FNAME LP mod_list RP { arscp_addlib($2, $4); } 168 | ADDLIB FNAME { arscp_addlib($2, NULL); } 169 ; 170 171 addmod_cmd 172 : ADDMOD mod_list { arscp_addmod($2); } 173 ; 174 175 clear_cmd 176 : CLEAR { arscp_clear(); } 177 ; 178 179 create_cmd 180 : CREATE FNAME { arscp_create(NULL, $2); } 181 ; 182 183 delete_cmd 184 : DELETE mod_list { arscp_delete($2); } 185 ; 186 187 directory_cmd 188 : DIRECTORY FNAME { arscp_dir($2, NULL, NULL); } 189 | DIRECTORY FNAME LP mod_list RP { arscp_dir($2, $4, NULL); } 190 | DIRECTORY FNAME LP mod_list RP FNAME { arscp_dir($2, $4, $6); } 191 ; 192 193 end_cmd 194 : END { arscp_end(EXIT_SUCCESS); } 195 ; 196 197 extract_cmd 198 : EXTRACT mod_list { arscp_extract($2); } 199 ; 200 201 list_cmd 202 : LIST { arscp_list(); } 203 ; 204 205 open_cmd 206 : OPEN FNAME { arscp_open($2); } 207 ; 208 209 replace_cmd 210 : REPLACE mod_list { arscp_replace($2); } 211 ; 212 213 save_cmd 214 : SAVE { arscp_save(); } 215 ; 216 217 verbose_cmd 218 : VERBOSE { verbose = !verbose; } 219 ; 220 221 empty_cmd 222 : 223 ; 224 225 invalid_cmd 226 : FNAME { yyerror(NULL); } 227 ; 228 229 %% 230 231 /* ARGSUSED */ 232 static void 233 yyerror(const char *s) 234 { 235 236 (void) s; 237 printf("Syntax error in archive script, line %d\n", lineno); 238 } 239 240 /* 241 * The arscp_open() function will first open an archive and check its 242 * validity. If the archive format is valid, it will call 243 * arscp_create() to create a temporary copy of the archive. 244 */ 245 static void 246 arscp_open(char *fname) 247 { 248 struct archive *a; 249 struct archive_entry *entry; 250 int r; 251 252 if ((a = archive_read_new()) == NULL) 253 bsdar_errc(bsdar, 0, "archive_read_new failed"); 254 archive_read_support_format_ar(a); 255 AC(archive_read_open_filename(a, fname, DEF_BLKSZ)); 256 if ((r = archive_read_next_header(a, &entry))) 257 bsdar_warnc(bsdar, 0, "%s", archive_error_string(a)); 258 AC(archive_read_close(a)); 259 ACV(archive_read_free(a)); 260 if (r != ARCHIVE_OK) 261 return; 262 arscp_create(fname, fname); 263 } 264 265 /* 266 * Create an archive. 267 * 268 * If the parameter 'in' is NULL (the 'CREATE' command), a new empty 269 * archive will be created. If the parameter 'in' is not NULL (the 270 * 'OPEN' command), the resulting archive will be a modified version 271 * of the existing archive. 272 */ 273 static void 274 arscp_create(char *in, char *out) 275 { 276 struct archive *a; 277 int ifd, ofd; 278 279 /* Delete the previously created temporary archive, if any. */ 280 if (tmpac) { 281 if (unlink(tmpac) < 0) 282 bsdar_errc(bsdar, errno, "unlink failed"); 283 free(tmpac); 284 } 285 286 tmpac = strdup(TEMPLATE); 287 if (tmpac == NULL) 288 bsdar_errc(bsdar, errno, "strdup failed"); 289 if ((ofd = mkstemp(tmpac)) < 0) 290 bsdar_errc(bsdar, errno, "mkstemp failed"); 291 292 if (in) { 293 /* 294 * The 'OPEN' command creates a temporary copy of the 295 * input archive. 296 */ 297 if ((ifd = open(in, O_RDONLY)) < 0 || 298 elftc_copyfile(ifd, ofd) < 0) { 299 bsdar_warnc(bsdar, errno, "'OPEN' failed"); 300 (void) close(ofd); 301 if (ifd != -1) 302 (void) close(ifd); 303 return; 304 } 305 (void) close(ifd); 306 (void) close(ofd); 307 } else { 308 /* 309 * The 'CREATE' command creates an "empty" archive (an 310 * archive consisting only of the archive header). 311 */ 312 if ((a = archive_write_new()) == NULL) 313 bsdar_errc(bsdar, 0, "archive_write_new failed"); 314 archive_write_set_format_ar_svr4(a); 315 AC(archive_write_open_fd(a, ofd)); 316 AC(archive_write_close(a)); 317 ACV(archive_write_free(a)); 318 } 319 320 /* Override the previous target, if any. */ 321 if (target) 322 free(target); 323 324 target = out; 325 bsdar->filename = tmpac; 326 } 327 328 /* 329 * Add all modules of an archive to the current archive. If the 330 * parameter 'list' is not NULL, only those modules specified by 331 * 'list' will be added. 332 */ 333 static void 334 arscp_addlib(char *archive, struct list *list) 335 { 336 337 if (!arscp_target_exist()) 338 return; 339 arscp_mlist2argv(list); 340 bsdar->addlib = archive; 341 ar_write_archive(bsdar, 'A'); 342 arscp_free_argv(); 343 arscp_free_mlist(list); 344 } 345 346 /* 347 * Add modules to the current archive. 348 */ 349 static void 350 arscp_addmod(struct list *list) 351 { 352 353 if (!arscp_target_exist()) 354 return; 355 arscp_mlist2argv(list); 356 ar_write_archive(bsdar, 'q'); 357 arscp_free_argv(); 358 arscp_free_mlist(list); 359 } 360 361 /* 362 * Delete modules from the current archive. 363 */ 364 static void 365 arscp_delete(struct list *list) 366 { 367 368 if (!arscp_target_exist()) 369 return; 370 arscp_mlist2argv(list); 371 ar_write_archive(bsdar, 'd'); 372 arscp_free_argv(); 373 arscp_free_mlist(list); 374 } 375 376 /* 377 * Extract modules from the current archive. 378 */ 379 static void 380 arscp_extract(struct list *list) 381 { 382 383 if (!arscp_target_exist()) 384 return; 385 arscp_mlist2argv(list); 386 ar_read_archive(bsdar, 'x'); 387 arscp_free_argv(); 388 arscp_free_mlist(list); 389 } 390 391 /* 392 * List the contents of an archive (simple mode). 393 */ 394 static void 395 arscp_list(void) 396 { 397 398 if (!arscp_target_exist()) 399 return; 400 bsdar->argc = 0; 401 bsdar->argv = NULL; 402 /* Always verbose. */ 403 bsdar->options |= AR_V; 404 ar_read_archive(bsdar, 't'); 405 bsdar->options &= ~AR_V; 406 } 407 408 /* 409 * List the contents of an archive (advanced mode). 410 */ 411 static void 412 arscp_dir(char *archive, struct list *list, char *rlt) 413 { 414 FILE *out; 415 416 /* If rlt != NULL, redirect the output to it. */ 417 out = NULL; 418 if (rlt) { 419 out = bsdar->output; 420 if ((bsdar->output = fopen(rlt, "w")) == NULL) 421 bsdar_errc(bsdar, errno, "fopen %s failed", rlt); 422 } 423 424 bsdar->filename = archive; 425 if (list) 426 arscp_mlist2argv(list); 427 else { 428 bsdar->argc = 0; 429 bsdar->argv = NULL; 430 } 431 if (verbose) 432 bsdar->options |= AR_V; 433 ar_read_archive(bsdar, 't'); 434 bsdar->options &= ~AR_V; 435 436 if (rlt) { 437 if (fclose(bsdar->output) == EOF) 438 bsdar_errc(bsdar, errno, "fclose %s failed", rlt); 439 bsdar->output = out; 440 free(rlt); 441 } 442 free(archive); 443 bsdar->filename = tmpac; 444 arscp_free_argv(); 445 arscp_free_mlist(list); 446 } 447 448 449 /* 450 * Replace modules in the current archive. 451 */ 452 static void 453 arscp_replace(struct list *list) 454 { 455 456 if (!arscp_target_exist()) 457 return; 458 arscp_mlist2argv(list); 459 ar_write_archive(bsdar, 'r'); 460 arscp_free_argv(); 461 arscp_free_mlist(list); 462 } 463 464 /* 465 * Rename the temporary archive to the target archive. 466 */ 467 static void 468 arscp_save(void) 469 { 470 mode_t mask; 471 472 if (target) { 473 if (rename(tmpac, target) < 0) 474 bsdar_errc(bsdar, errno, "rename failed"); 475 /* 476 * Because mkstemp() creates temporary files with mode 477 * 0600, we set target archive's mode as per the 478 * process umask. 479 */ 480 mask = umask(0); 481 umask(mask); 482 if (chmod(target, 0666 & ~mask) < 0) 483 bsdar_errc(bsdar, errno, "chmod failed"); 484 free(tmpac); 485 free(target); 486 tmpac = NULL; 487 target= NULL; 488 bsdar->filename = NULL; 489 } else 490 bsdar_warnc(bsdar, 0, "no open output archive"); 491 } 492 493 /* 494 * Discard the contents of the current archive. This is achieved by 495 * invoking the 'CREATE' cmd on the current archive. 496 */ 497 static void 498 arscp_clear(void) 499 { 500 char *new_target; 501 502 if (target) { 503 new_target = strdup(target); 504 if (new_target == NULL) 505 bsdar_errc(bsdar, errno, "strdup failed"); 506 arscp_create(NULL, new_target); 507 } 508 } 509 510 /* 511 * Quit ar(1). Note that the 'END' cmd will not 'SAVE' the current 512 * archive before exiting. 513 */ 514 static void 515 arscp_end(int eval) 516 { 517 518 if (target) 519 free(target); 520 if (tmpac) { 521 if (unlink(tmpac) == -1) 522 bsdar_errc(bsdar, errno, "unlink %s failed", tmpac); 523 free(tmpac); 524 } 525 526 exit(eval); 527 } 528 529 /* 530 * Check if a target was specified, i.e, whether an 'OPEN' or 'CREATE' 531 * had been issued by the user. 532 */ 533 static int 534 arscp_target_exist(void) 535 { 536 537 if (target) 538 return (1); 539 540 bsdar_warnc(bsdar, 0, "no open output archive"); 541 return (0); 542 } 543 544 /* 545 * Construct the list of modules. 546 */ 547 static struct list * 548 arscp_mlist(struct list *list, char *str) 549 { 550 struct list *l; 551 552 l = malloc(sizeof(*l)); 553 if (l == NULL) 554 bsdar_errc(bsdar, errno, "malloc failed"); 555 l->str = str; 556 l->next = list; 557 558 return (l); 559 } 560 561 /* 562 * Calculate the length of an mlist. 563 */ 564 static int 565 arscp_mlist_len(struct list *list) 566 { 567 int len; 568 569 for(len = 0; list; list = list->next) 570 len++; 571 572 return (len); 573 } 574 575 /* 576 * Free the space allocated for a module list. 577 */ 578 static void 579 arscp_free_mlist(struct list *list) 580 { 581 struct list *l; 582 583 /* Note: list->str was freed in arscp_free_argv(). */ 584 for(; list; list = l) { 585 l = list->next; 586 free(list); 587 } 588 } 589 590 /* 591 * Convert a module list to an 'argv' array. 592 */ 593 static void 594 arscp_mlist2argv(struct list *list) 595 { 596 char **argv; 597 int i, n; 598 599 n = arscp_mlist_len(list); 600 argv = malloc(n * sizeof(*argv)); 601 if (argv == NULL) 602 bsdar_errc(bsdar, errno, "malloc failed"); 603 604 /* Note that module names are stored in reverse order. */ 605 for(i = n - 1; i >= 0; i--, list = list->next) { 606 if (list == NULL) 607 bsdar_errc(bsdar, errno, "invalid mlist"); 608 argv[i] = list->str; 609 } 610 611 bsdar->argc = n; 612 bsdar->argv = argv; 613 } 614 615 /* 616 * Free the space allocated for an argv array and its elements. 617 */ 618 static void 619 arscp_free_argv(void) 620 { 621 int i; 622 623 for(i = 0; i < bsdar->argc; i++) 624 free(bsdar->argv[i]); 625 626 free(bsdar->argv); 627 } 628 629 /* 630 * Show a prompt if we are in interactive mode. 631 */ 632 static void 633 arscp_prompt(void) 634 { 635 636 if (interactive) { 637 printf("AR >"); 638 fflush(stdout); 639 } 640 } 641 642 /* 643 * The main function implementing script mode. 644 */ 645 void 646 ar_mode_script(struct bsdar *ar) 647 { 648 649 bsdar = ar; 650 interactive = isatty(fileno(stdin)); 651 while(yyparse()) { 652 if (!interactive) 653 arscp_end(EXIT_FAILURE); 654 } 655 656 /* Script ends without END */ 657 arscp_end(EXIT_SUCCESS); 658 } 659