Home | History | Annotate | Download | only in src
      1 /* Create, modify, and extract from archives.
      2    Copyright (C) 2005-2012 Red Hat, Inc.
      3    Written by Ulrich Drepper <drepper (at) redhat.com>, 2005.
      4 
      5    Red Hat elfutils is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by the
      7    Free Software Foundation; version 2 of the License.
      8 
      9    Red Hat elfutils is distributed in the hope that it will be useful, but
     10    WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License along
     15    with Red Hat elfutils; if not, write to the Free Software Foundation,
     16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     17 
     18    Red Hat elfutils is an included package of the Open Invention Network.
     19    An included package of the Open Invention Network is a package for which
     20    Open Invention Network licensees cross-license their patents.  No patent
     21    license is granted, either expressly or impliedly, by designation as an
     22    included package.  Should you wish to participate in the Open Invention
     23    Network licensing program, please visit www.openinventionnetwork.com
     24    <http://www.openinventionnetwork.com>.  */
     25 
     26 #ifdef HAVE_CONFIG_H
     27 # include <config.h>
     28 #endif
     29 
     30 #include <argp.h>
     31 #include <assert.h>
     32 #include <error.h>
     33 #include <fcntl.h>
     34 #include <gelf.h>
     35 #include <libintl.h>
     36 #include <limits.h>
     37 #include <locale.h>
     38 #include <mcheck.h>
     39 #include <search.h>
     40 #include <stdbool.h>
     41 #include <stdlib.h>
     42 #include <stdio.h>
     43 #include <stdio_ext.h>
     44 #include <string.h>
     45 #include <time.h>
     46 #include <unistd.h>
     47 #include <sys/mman.h>
     48 #include <sys/stat.h>
     49 #include <sys/time.h>
     50 
     51 #include <system.h>
     52 
     53 #include "arlib.h"
     54 
     55 
     56 /* Name and version of program.  */
     57 static void print_version (FILE *stream, struct argp_state *state);
     58 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
     59 
     60 /* Prototypes for local functions.  */
     61 static int do_oper_extract (int oper, const char *arfname, char **argv,
     62 			    int argc, long int instance);
     63 static int do_oper_delete (const char *arfname, char **argv, int argc,
     64 			   long int instance);
     65 static int do_oper_insert (int oper, const char *arfname, char **argv,
     66 			   int argc, const char *member);
     67 
     68 
     69 /* Bug report address.  */
     70 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
     71 
     72 
     73 /* Definitions of arguments for argp functions.  */
     74 static const struct argp_option options[] =
     75 {
     76   { NULL, 0, NULL, 0, N_("Commands:"), 1 },
     77   { NULL, 'd', NULL, 0, N_("Delete files from archive."), 0 },
     78   { NULL, 'm', NULL, 0, N_("Move files in archive."), 0 },
     79   { NULL, 'p', NULL, 0, N_("Print files in archive."), 0 },
     80   { NULL, 'q', NULL, 0, N_("Quick append files to archive."), 0 },
     81   { NULL, 'r', NULL, 0,
     82     N_("Replace existing or insert new file into archive."), 0 },
     83   { NULL, 't', NULL, 0, N_("Display content of archive."), 0 },
     84   { NULL, 'x', NULL, 0, N_("Extract files from archive."), 0 },
     85 
     86   { NULL, 0, NULL, 0, N_("Command Modifiers:"), 2 },
     87   { NULL, 'o', NULL, 0, N_("Preserve original dates."), 0 },
     88   { NULL, 'N', NULL, 0, N_("Use instance [COUNT] of name."), 0 },
     89   { NULL, 'C', NULL, 0,
     90     N_("Do not replace existing files with extracted files."), 0 },
     91   { NULL, 'T', NULL, 0, N_("Allow filename to be truncated if necessary."),
     92     0 },
     93   { NULL, 'v', NULL, 0, N_("Provide verbose output."), 0 },
     94   { NULL, 's', NULL, 0, N_("Force regeneration of symbol table."), 0 },
     95   { NULL, 'a', NULL, 0, N_("Insert file after [MEMBER]."), 0 },
     96   { NULL, 'b', NULL, 0, N_("Insert file before [MEMBER]."), 0 },
     97   { NULL, 'i', NULL, 0, N_("Same as -b."), 0 },
     98   { NULL, 'c', NULL, 0, N_("Suppress message when library has to be created."),
     99     0 },
    100   { NULL, 'P', NULL, 0, N_("Use full path for file matching."), 0 },
    101   { NULL, 'u', NULL, 0, N_("Update only older files in archive."), 0 },
    102 
    103   { NULL, 0, NULL, 0, NULL, 0 }
    104 };
    105 
    106 /* Short description of program.  */
    107 static const char doc[] = N_("Create, modify, and extract from archives.");
    108 
    109 /* Strings for arguments in help texts.  */
    110 static const char args_doc[] = N_("[MEMBER] [COUNT] ARCHIVE [FILE...]");
    111 
    112 /* Prototype for option handler.  */
    113 static error_t parse_opt (int key, char *arg, struct argp_state *state);
    114 
    115 /* Data structure to communicate with argp functions.  */
    116 static struct argp argp =
    117 {
    118   options, parse_opt, args_doc, doc, arlib_argp_children, NULL, NULL
    119 };
    120 
    121 
    122 /* What operation to perform.  */
    123 static enum
    124   {
    125     oper_none,
    126     oper_delete,
    127     oper_move,
    128     oper_print,
    129     oper_qappend,
    130     oper_replace,
    131     oper_list,
    132     oper_extract
    133   } operation;
    134 
    135 /* Modifiers.  */
    136 static bool verbose;
    137 static bool preserve_dates;
    138 static bool instance_specifed;
    139 static bool dont_replace_existing;
    140 static bool allow_truncate_fname;
    141 static bool force_symtab;
    142 static bool suppress_create_msg;
    143 static bool full_path;
    144 static bool update_newer;
    145 static enum { ipos_none, ipos_before, ipos_after } ipos;
    146 
    147 
    148 int
    149 main (int argc, char *argv[])
    150 {
    151   /* Make memory leak detection possible.  */
    152   mtrace ();
    153 
    154   /* We use no threads here which can interfere with handling a stream.  */
    155   (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER);
    156   (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
    157   (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER);
    158 
    159   /* Set locale.  */
    160   (void) setlocale (LC_ALL, "");
    161 
    162   /* Make sure the message catalog can be found.  */
    163   (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
    164 
    165   /* Initialize the message catalog.  */
    166   (void) textdomain (PACKAGE_TARNAME);
    167 
    168   /* For historical reasons the options in the first parameter need
    169      not be preceded by a dash.  Add it now if necessary.  */
    170   if (argc > 1 && argv[1][0] != '-')
    171     {
    172       size_t len = strlen (argv[1]) + 1;
    173       char *newp = alloca (len + 1);
    174       newp[0] = '-';
    175       memcpy (&newp[1], argv[1], len);
    176       argv[1] = newp;
    177     }
    178 
    179   /* Parse and process arguments.  */
    180   int remaining;
    181   (void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &remaining, NULL);
    182 
    183   /* Tell the library which version we are expecting.  */
    184   (void) elf_version (EV_CURRENT);
    185 
    186   /* Handle the [MEMBER] parameter.  */
    187   const char *member = NULL;
    188   if (ipos != ipos_none)
    189     {
    190       /* Only valid for certain operations.  */
    191       if (operation != oper_move && operation != oper_replace)
    192 	error (1, 0, gettext ("\
    193 'a', 'b', and 'i' are only allowed with the 'm' and 'r' options"));
    194 
    195       if (remaining == argc)
    196 	{
    197 	  error (0, 0, gettext ("\
    198 MEMBER parameter required for 'a', 'b', and 'i' modifiers"));
    199 	  argp_help (&argp, stderr, ARGP_HELP_USAGE | ARGP_HELP_SEE,
    200 		     program_invocation_short_name);
    201 	  exit (EXIT_FAILURE);
    202 	}
    203 
    204       member = argv[remaining++];
    205     }
    206 
    207   /* Handle the [COUNT] parameter.  */
    208   long int instance = -1;
    209   if (instance_specifed)
    210     {
    211       /* Only valid for certain operations.  */
    212       if (operation == oper_extract && operation == oper_delete)
    213 	error (1, 0, gettext ("\
    214 'N' is only meaningful with the 'x' and 'd' options"));
    215 
    216       if (remaining == argc)
    217 	{
    218 	  error (0, 0, gettext ("COUNT parameter required"));
    219 	  argp_help (&argp, stderr, ARGP_HELP_SEE,
    220 		     program_invocation_short_name);
    221 	  exit (EXIT_FAILURE);
    222 	}
    223 
    224       char *endp;
    225       errno = 0;
    226       if (((instance = strtol (argv[remaining], &endp, 10)) == LONG_MAX
    227 	   && errno == ERANGE)
    228 	  || instance <= 0
    229 	  || *endp != '\0')
    230 	error (1, 0, gettext ("invalid COUNT parameter %s"), argv[remaining]);
    231 
    232       ++remaining;
    233     }
    234 
    235   if ((dont_replace_existing || allow_truncate_fname)
    236       && unlikely (operation != oper_extract))
    237     error (1, 0, gettext ("'%c' is only meaningful with the 'x' option"),
    238 	   dont_replace_existing ? 'C' : 'T');
    239 
    240   /* There must at least be one more parameter specifying the archive.   */
    241   if (remaining == argc)
    242     {
    243       error (0, 0, gettext ("archive name required"));
    244       argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
    245       exit (EXIT_FAILURE);
    246     }
    247 
    248   const char *arfname = argv[remaining++];
    249   argv += remaining;
    250   argc -= remaining;
    251 
    252   int status;
    253   switch (operation)
    254     {
    255     case oper_none:
    256       error (0, 0, gettext ("command option required"));
    257       argp_help (&argp, stderr, ARGP_HELP_STD_ERR,
    258 		 program_invocation_short_name);
    259       status = 1;
    260       break;
    261 
    262     case oper_list:
    263     case oper_print:
    264       status = do_oper_extract (operation, arfname, argv, argc, -1);
    265       break;
    266 
    267     case oper_extract:
    268       status = do_oper_extract (operation, arfname, argv, argc, instance);
    269       break;
    270 
    271     case oper_delete:
    272       status = do_oper_delete (arfname, argv, argc, instance);
    273       break;
    274 
    275     case oper_move:
    276     case oper_qappend:
    277     case oper_replace:
    278       status = do_oper_insert (operation, arfname, argv, argc, member);
    279       break;
    280 
    281     default:
    282       assert (! "should not happen");
    283       status = 1;
    284       break;
    285     }
    286 
    287   return status;
    288 }
    289 
    290 
    291 /* Print the version information.  */
    292 static void
    293 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
    294 {
    295   fprintf (stream, "ar (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
    296   fprintf (stream, gettext ("\
    297 Copyright (C) %s Red Hat, Inc.\n\
    298 This is free software; see the source for copying conditions.  There is NO\n\
    299 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
    300 "), "2012");
    301   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
    302 }
    303 
    304 
    305 /* Handle program arguments.  */
    306 static error_t
    307 parse_opt (int key, char *arg __attribute__ ((unused)),
    308 	   struct argp_state *state __attribute__ ((unused)))
    309 {
    310   switch (key)
    311     {
    312     case 'd':
    313     case 'm':
    314     case 'p':
    315     case 'q':
    316     case 'r':
    317     case 't':
    318     case 'x':
    319       if (operation != oper_none)
    320 	{
    321 	  error (0, 0, gettext ("More than one operation specified"));
    322 	  argp_help (&argp, stderr, ARGP_HELP_SEE,
    323 		     program_invocation_short_name);
    324 	  exit (EXIT_FAILURE);
    325 	}
    326 
    327       switch (key)
    328 	{
    329 	case 'd':
    330 	  operation = oper_delete;
    331 	  break;
    332 	case 'm':
    333 	  operation = oper_move;
    334 	  break;
    335 	case 'p':
    336 	  operation = oper_print;
    337 	  break;
    338 	case 'q':
    339 	  operation = oper_qappend;
    340 	  break;
    341 	case 'r':
    342 	  operation = oper_replace;
    343 	  break;
    344 	case 't':
    345 	  operation = oper_list;
    346 	  break;
    347 	case 'x':
    348 	  operation = oper_extract;
    349 	  break;
    350 	}
    351       break;
    352 
    353     case 'a':
    354       ipos = ipos_after;
    355       break;
    356 
    357     case 'b':
    358     case 'i':
    359       ipos = ipos_before;
    360       break;
    361 
    362     case 'c':
    363       suppress_create_msg = true;
    364       break;
    365 
    366     case 'C':
    367       dont_replace_existing = true;
    368       break;
    369 
    370     case 'N':
    371       instance_specifed = true;
    372       break;
    373 
    374     case 'o':
    375       preserve_dates = true;
    376       break;
    377 
    378     case 'P':
    379       full_path = true;
    380       break;
    381 
    382     case 's':
    383       force_symtab = true;
    384       break;
    385 
    386     case 'T':
    387       allow_truncate_fname = true;
    388       break;
    389 
    390     case 'u':
    391       update_newer = true;
    392       break;
    393 
    394     case 'v':
    395       verbose = true;
    396       break;
    397 
    398     default:
    399       return ARGP_ERR_UNKNOWN;
    400     }
    401   return 0;
    402 }
    403 
    404 
    405 static int
    406 open_archive (const char *arfname, int flags, int mode, Elf **elf,
    407 	      struct stat *st, bool miss_allowed)
    408 {
    409   int fd = open (arfname, flags, mode);
    410   if (fd == -1)
    411     {
    412       if (miss_allowed)
    413 	return -1;
    414 
    415       error (EXIT_FAILURE, errno, gettext ("cannot open archive '%s'"),
    416 	     arfname);
    417     }
    418 
    419   if (elf != NULL)
    420     {
    421       Elf_Cmd cmd = flags == O_RDONLY ? ELF_C_READ_MMAP : ELF_C_RDWR_MMAP;
    422 
    423       *elf = elf_begin (fd, cmd, NULL);
    424       if (*elf == NULL)
    425 	error (EXIT_FAILURE, 0, gettext ("cannot open archive '%s': %s"),
    426 	       arfname, elf_errmsg (-1));
    427 
    428       if (flags == O_RDONLY && elf_kind (*elf) != ELF_K_AR)
    429 	error (EXIT_FAILURE, 0, gettext ("%s: not an archive file"), arfname);
    430     }
    431 
    432   if (st != NULL && fstat (fd, st) != 0)
    433     error (EXIT_FAILURE, errno, gettext ("cannot stat archive '%s'"),
    434 	   arfname);
    435 
    436   return fd;
    437 }
    438 
    439 
    440 static void
    441 not_found (int argc, char *argv[argc], bool found[argc])
    442 {
    443   for (int i = 0; i < argc; ++i)
    444     if (!found[i])
    445       printf (gettext ("no entry %s in archive\n"), argv[i]);
    446 }
    447 
    448 
    449 static int
    450 copy_content (Elf *elf, int newfd, off_t off, size_t n)
    451 {
    452   size_t len;
    453   char *rawfile = elf_rawfile (elf, &len);
    454 
    455   assert (off + n <= len);
    456 
    457   /* Tell the kernel we will read all the pages sequentially.  */
    458   size_t ps = sysconf (_SC_PAGESIZE);
    459   if (n > 2 * ps)
    460     posix_madvise (rawfile + (off & ~(ps - 1)), n, POSIX_MADV_SEQUENTIAL);
    461 
    462   return write_retry (newfd, rawfile + off, n) != (ssize_t) n;
    463 }
    464 
    465 
    466 static int
    467 do_oper_extract (int oper, const char *arfname, char **argv, int argc,
    468 		 long int instance)
    469 {
    470   bool found[argc];
    471   memset (found, '\0', sizeof (found));
    472 
    473   size_t name_max = 0;
    474   inline bool should_truncate_fname (void)
    475   {
    476     if (errno == ENAMETOOLONG && allow_truncate_fname)
    477       {
    478 	if (name_max == 0)
    479 	  {
    480 	    long int len = pathconf (".", _PC_NAME_MAX);
    481 	    if (len > 0)
    482 	      name_max = len;
    483 	  }
    484 	return name_max != 0;
    485       }
    486     return false;
    487   }
    488 
    489   off_t index_off = -1;
    490   size_t index_size = 0;
    491   off_t cur_off = SARMAG;
    492 
    493   int status = 0;
    494   Elf *elf;
    495   int fd = open_archive (arfname, O_RDONLY, 0, &elf, NULL, false);
    496 
    497   if (hcreate (2 * argc) == 0)
    498     error (EXIT_FAILURE, errno, gettext ("cannot create hash table"));
    499 
    500   for (int cnt = 0; cnt < argc; ++cnt)
    501     {
    502       ENTRY entry = { .key = argv[cnt], .data = &argv[cnt] };
    503       if (hsearch (entry, ENTER) == NULL)
    504 	error (EXIT_FAILURE, errno,
    505 	       gettext ("cannot insert into hash table"));
    506     }
    507 
    508   struct stat st;
    509   if (force_symtab)
    510     {
    511       if (fstat (fd, &st) != 0)
    512 	{
    513 	  error (0, errno, gettext ("cannot stat '%s'"), arfname);
    514 	  close (fd);
    515 	  return 1;
    516 	}
    517       arlib_init ();
    518     }
    519 
    520   Elf_Cmd cmd = ELF_C_READ_MMAP;
    521   Elf *subelf;
    522   while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
    523     {
    524       Elf_Arhdr *arhdr = elf_getarhdr (subelf);
    525 
    526       if (strcmp (arhdr->ar_name, "/") == 0)
    527 	{
    528 	  index_off = elf_getaroff (subelf);
    529 	  index_size = arhdr->ar_size;
    530 	  goto next;
    531 	}
    532       if (strcmp (arhdr->ar_name, "//") == 0)
    533 	goto next;
    534 
    535       if (force_symtab)
    536 	{
    537 	  arlib_add_symbols (elf, arfname, arhdr->ar_name, cur_off);
    538 	  cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1))
    539 		      + sizeof (struct ar_hdr));
    540 	}
    541 
    542       bool do_extract = argc <= 0;
    543       if (!do_extract)
    544 	{
    545 	  ENTRY entry;
    546 	  entry.key = arhdr->ar_name;
    547 	  ENTRY *res = hsearch (entry, FIND);
    548 	  if (res != NULL && (instance < 0 || instance-- == 0)
    549 	      && !found[(char **) res->data - argv])
    550 	    found[(char **) res->data - argv] = do_extract = true;
    551 	}
    552 
    553       if (do_extract)
    554 	{
    555 	  if (verbose)
    556 	    {
    557 	      if (oper == oper_print)
    558 		{
    559 		  printf ("\n<%s>\n\n", arhdr->ar_name);
    560 
    561 		  /* We have to flush now because now we use the descriptor
    562 		     directly.  */
    563 		  fflush (stdout);
    564 		}
    565 	      else if (oper == oper_list)
    566 		{
    567 		  char datestr[100];
    568 		  strftime (datestr, sizeof (datestr), "%b %e %H:%M %Y",
    569 			    localtime (&arhdr->ar_date));
    570 
    571 		  printf ("%c%c%c%c%c%c%c%c%c %u/%u %6ju %s %s\n",
    572 			  (arhdr->ar_mode & S_IRUSR) ? 'r' : '-',
    573 			  (arhdr->ar_mode & S_IWUSR) ? 'w' : '-',
    574 			  (arhdr->ar_mode & S_IXUSR)
    575 			  ? ((arhdr->ar_mode & S_ISUID) ? 's' : 'x')
    576 			  : ((arhdr->ar_mode & S_ISUID) ? 'S' : '-'),
    577 			  (arhdr->ar_mode & S_IRGRP) ? 'r' : '-',
    578 			  (arhdr->ar_mode & S_IWGRP) ? 'w' : '-',
    579 			  (arhdr->ar_mode & S_IXGRP)
    580 			  ? ((arhdr->ar_mode & S_ISGID) ? 's' : 'x')
    581 			  : ((arhdr->ar_mode & S_ISGID) ? 'S' : '-'),
    582 			  (arhdr->ar_mode & S_IROTH) ? 'r' : '-',
    583 			  (arhdr->ar_mode & S_IWOTH) ? 'w' : '-',
    584 			  (arhdr->ar_mode & S_IXOTH)
    585 			  ? ((arhdr->ar_mode & S_ISVTX) ? 't' : 'x')
    586 			  : ((arhdr->ar_mode & S_ISVTX) ? 'T' : '-'),
    587 			  arhdr->ar_uid,
    588 			  arhdr->ar_gid,
    589 			  (uintmax_t) arhdr->ar_size,
    590 			  datestr,
    591 			  arhdr->ar_name);
    592 		}
    593 	      else
    594 		printf ("x - %s\n", arhdr->ar_name);
    595 	    }
    596 
    597 	  if (oper == oper_list)
    598 	    {
    599 	      if (!verbose)
    600 		puts (arhdr->ar_name);
    601 
    602 	      goto next;
    603 	    }
    604 
    605 	  size_t nleft;
    606 	  char *data = elf_rawfile (subelf, &nleft);
    607 	  if (data == NULL)
    608 	    {
    609 	      error (0, 0, gettext ("cannot read content of %s: %s"),
    610 		     arhdr->ar_name, elf_errmsg (-1));
    611 	      status = 1;
    612 	      goto next;
    613 	    }
    614 
    615 	  int xfd;
    616 	  char tempfname[] = "XXXXXX";
    617 	  bool use_mkstemp = true;
    618 
    619 	  if (oper == oper_print)
    620 	    xfd = STDOUT_FILENO;
    621 	  else
    622 	    {
    623 	      xfd = mkstemp (tempfname);
    624 	      if (unlikely (xfd == -1))
    625 		{
    626 		  /* We cannot create a temporary file.  Try to overwrite
    627 		     the file or create it if it does not exist.  */
    628 		  int flags = O_WRONLY | O_CREAT;
    629 		  if (dont_replace_existing)
    630 		    flags |= O_EXCL;
    631 		  else
    632 		    flags |= O_TRUNC;
    633 		  xfd = open (arhdr->ar_name, flags, 0600);
    634 		  if (unlikely (xfd == -1))
    635 		    {
    636 		      int printlen = INT_MAX;
    637 
    638 		      if (should_truncate_fname ())
    639 			{
    640 			  /* Try to truncate the name.  First find out by how
    641 			     much.  */
    642 			  printlen = name_max;
    643 			  char truncfname[name_max + 1];
    644 			  *((char *) mempcpy (truncfname, arhdr->ar_name,
    645 					      name_max)) = '\0';
    646 
    647 			  xfd = open (truncfname, flags, 0600);
    648 			}
    649 
    650 		      if (xfd == -1)
    651 			{
    652 			  error (0, errno, gettext ("cannot open %.*s"),
    653 				 (int) printlen, arhdr->ar_name);
    654 			  status = 1;
    655 			  goto next;
    656 			}
    657 		    }
    658 
    659 		  use_mkstemp = false;
    660 		}
    661 	    }
    662 
    663 	  ssize_t n;
    664 	  while ((n = TEMP_FAILURE_RETRY (write (xfd, data, nleft))) != -1)
    665 	    {
    666 	      nleft -= n;
    667 	      if (nleft == 0)
    668 		break;
    669 	      data += n;
    670 	    }
    671 
    672 	  if (unlikely (n == -1))
    673 	    {
    674 	      error (0, errno, gettext ("failed to write %s"), arhdr->ar_name);
    675 	      status = 1;
    676 	      unlink (tempfname);
    677 	      close (xfd);
    678 	      goto next;
    679 	    }
    680 
    681 	  if (oper != oper_print)
    682 	    {
    683 	      /* Fix up the mode.  */
    684 	      if (unlikely (fchmod (xfd, arhdr->ar_mode) != 0))
    685 		{
    686 		  error (0, errno, gettext ("cannot change mode of %s"),
    687 			 arhdr->ar_name);
    688 		  status = 0;
    689 		}
    690 
    691 	      if (preserve_dates)
    692 		{
    693 		  struct timeval tv[2];
    694 		  tv[0].tv_sec = arhdr->ar_date;
    695 		  tv[0].tv_usec = 0;
    696 		  tv[1].tv_sec = arhdr->ar_date;
    697 		  tv[1].tv_usec = 0;
    698 
    699 		  if (unlikely (futimes (xfd, tv) != 0))
    700 		    {
    701 		      error (0, errno,
    702 			     gettext ("cannot change modification time of %s"),
    703 			     arhdr->ar_name);
    704 		      status = 1;
    705 		    }
    706 		}
    707 
    708 	      /* If we used a temporary file, move it do the right
    709 		 name now.  */
    710 	      if (use_mkstemp)
    711 		{
    712 		  int r;
    713 
    714 		  if (dont_replace_existing)
    715 		    {
    716 		      r = link (tempfname, arhdr->ar_name);
    717 		      if (likely (r == 0))
    718 			unlink (tempfname);
    719 		    }
    720 		  else
    721 		    r = rename (tempfname, arhdr->ar_name);
    722 
    723 		  if (unlikely (r) != 0)
    724 		    {
    725 		      int printlen = INT_MAX;
    726 
    727 		      if (should_truncate_fname ())
    728 			{
    729 			  /* Try to truncate the name.  First find out by how
    730 			     much.  */
    731 			  printlen = name_max;
    732 			  char truncfname[name_max + 1];
    733 			  *((char *) mempcpy (truncfname, arhdr->ar_name,
    734 					      name_max)) = '\0';
    735 
    736 			  if (dont_replace_existing)
    737 			    {
    738 			      r = link (tempfname, truncfname);
    739 			      if (likely (r == 0))
    740 				unlink (tempfname);
    741 			    }
    742 			  else
    743 			    r = rename (tempfname, truncfname);
    744 			}
    745 
    746 		      if (r != 0)
    747 			{
    748 			  error (0, errno, gettext ("\
    749 cannot rename temporary file to %.*s"),
    750 				 printlen, arhdr->ar_name);
    751 			  unlink (tempfname);
    752 			  status = 1;
    753 			}
    754 		    }
    755 		}
    756 
    757 	      close (xfd);
    758 	    }
    759 	}
    760 
    761     next:
    762       cmd = elf_next (subelf);
    763       if (elf_end (subelf) != 0)
    764 	error (1, 0, "%s: %s", arfname, elf_errmsg (-1));
    765     }
    766 
    767   hdestroy ();
    768 
    769   if (force_symtab)
    770     {
    771       arlib_finalize ();
    772 
    773       if (symtab.symsnamelen != 0
    774 	  /* We have to rewrite the file also if it initially had an index
    775 	     but now does not need one anymore.  */
    776 	  || (symtab.symsnamelen == 0 && index_size != 0))
    777 	{
    778 	  char tmpfname[strlen (arfname) + 7];
    779 	  strcpy (stpcpy (tmpfname, arfname), "XXXXXX");
    780 	  int newfd = mkstemp (tmpfname);
    781 	  if (unlikely (newfd == -1))
    782 	    {
    783 	    nonew:
    784 	      error (0, errno, gettext ("cannot create new file"));
    785 	      status = 1;
    786 	    }
    787 	  else
    788 	    {
    789 	      /* Create the header.  */
    790 	      if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
    791 		{
    792 		  // XXX Use /prof/self/fd/%d ???
    793 		nonew_unlink:
    794 		  unlink (tmpfname);
    795 		  if (newfd != -1)
    796 		    close (newfd);
    797 		  goto nonew;
    798 		}
    799 
    800 	      /* Create the new file.  There are three parts as far we are
    801 		 concerned: 1. original context before the index, 2. the
    802 		 new index, 3. everything after the new index.  */
    803 	      off_t rest_off;
    804 	      if (index_off != -1)
    805 		rest_off = (index_off + sizeof (struct ar_hdr)
    806 			    + ((index_size + 1) & ~1ul));
    807 	      else
    808 		rest_off = SARMAG;
    809 
    810 	      if ((symtab.symsnamelen != 0
    811 		   && ((write_retry (newfd, symtab.symsoff,
    812 				     symtab.symsofflen)
    813 			!= (ssize_t) symtab.symsofflen)
    814 		       || (write_retry (newfd, symtab.symsname,
    815 					symtab.symsnamelen)
    816 			   != (ssize_t) symtab.symsnamelen)))
    817 		  /* Even if the original file had content before the
    818 		     symbol table, we write it in the correct order.  */
    819 		  || (index_off != SARMAG
    820 		      && copy_content (elf, newfd, SARMAG, index_off - SARMAG))
    821 		  || copy_content (elf, newfd, rest_off, st.st_size - rest_off)
    822 		  /* Set the mode of the new file to the same values the
    823 		     original file has.  */
    824 		  || fchmod (newfd, st.st_mode & ALLPERMS) != 0
    825 		  /* Never complain about fchown failing.  */
    826 		  || (({asm ("" :: "r" (fchown (newfd, st.st_uid,
    827 						st.st_gid))); }),
    828 		      close (newfd) != 0)
    829 		  || (newfd = -1, rename (tmpfname, arfname) != 0))
    830 		goto nonew_unlink;
    831 	    }
    832 	}
    833     }
    834 
    835   elf_end (elf);
    836 
    837   close (fd);
    838 
    839   not_found (argc, argv, found);
    840 
    841   return status;
    842 }
    843 
    844 
    845 struct armem
    846 {
    847   off_t off;
    848   off_t old_off;
    849   size_t size;
    850   long int long_name_off;
    851   struct armem *next;
    852   void *mem;
    853   time_t sec;
    854   uid_t uid;
    855   gid_t gid;
    856   mode_t mode;
    857   const char *name;
    858   Elf *elf;
    859 };
    860 
    861 
    862 static int
    863 write_member (struct armem *memb, off_t *startp, off_t *lenp, Elf *elf,
    864 	      off_t end_off, int newfd)
    865 {
    866   struct ar_hdr arhdr;
    867   char tmpbuf[sizeof (arhdr.ar_name) + 1];
    868 
    869   bool changed_header = memb->long_name_off != -1;
    870   if (changed_header)
    871     {
    872       /* In case of a long file name we assume the archive header
    873 	 changed and we write it here.  */
    874       memcpy (&arhdr, elf_rawfile (elf, NULL) + *startp, sizeof (arhdr));
    875 
    876       snprintf (tmpbuf, sizeof (tmpbuf), "/%-*ld",
    877 		(int) sizeof (arhdr.ar_name), memb->long_name_off);
    878       changed_header = memcmp (arhdr.ar_name, tmpbuf,
    879 			       sizeof (arhdr.ar_name)) != 0;
    880     }
    881 
    882   /* If the files are adjacent in the old file extend the range.  */
    883   if (*startp != -1 && !changed_header && *startp + *lenp == memb->old_off)
    884     {
    885       /* Extend the current range.  */
    886       *lenp += (memb->next != NULL
    887 		? memb->next->off : end_off) - memb->off;
    888       return 0;
    889     }
    890 
    891   /* Write out the old range.  */
    892   if (*startp != -1 && copy_content (elf, newfd, *startp, *lenp))
    893     return -1;
    894 
    895   *startp = memb->old_off;
    896   *lenp = (memb->next != NULL ? memb->next->off : end_off) - memb->off;
    897 
    898   if (changed_header)
    899     {
    900       memcpy (arhdr.ar_name, tmpbuf, sizeof (arhdr.ar_name));
    901 
    902       if (unlikely (write_retry (newfd, &arhdr, sizeof (arhdr))
    903 		    != sizeof (arhdr)))
    904 	return -1;
    905 
    906       *startp += sizeof (struct ar_hdr);
    907       assert ((size_t) *lenp >= sizeof (struct ar_hdr));
    908       *lenp -= sizeof (struct ar_hdr);
    909     }
    910 
    911   return 0;
    912 }
    913 
    914 /* Store the name in the long name table if necessary.
    915    Record its offset or -1 if we did not need to use the table.  */
    916 static void
    917 remember_long_name (struct armem *mem, const char *name, size_t namelen)
    918 {
    919   mem->long_name_off = (namelen > MAX_AR_NAME_LEN
    920 			? arlib_add_long_name (name, namelen)
    921 			: -1l);
    922 }
    923 
    924 static int
    925 do_oper_delete (const char *arfname, char **argv, int argc,
    926 		long int instance)
    927 {
    928   bool *found = alloca (sizeof (bool) * argc);
    929   memset (found, '\0', sizeof (found));
    930 
    931   /* List of the files we keep.  */
    932   struct armem *to_copy = NULL;
    933 
    934   int status = 0;
    935   Elf *elf;
    936   struct stat st;
    937   int fd = open_archive (arfname, O_RDONLY, 0, &elf, &st, false);
    938 
    939   if (hcreate (2 * argc) == 0)
    940     error (EXIT_FAILURE, errno, gettext ("cannot create hash table"));
    941 
    942   for (int cnt = 0; cnt < argc; ++cnt)
    943     {
    944       ENTRY entry = { .key = argv[cnt], .data = &argv[cnt] };
    945       if (hsearch (entry, ENTER) == NULL)
    946 	error (EXIT_FAILURE, errno,
    947 	       gettext ("cannot insert into hash table"));
    948     }
    949 
    950   arlib_init ();
    951 
    952   off_t cur_off = SARMAG;
    953   Elf_Cmd cmd = ELF_C_READ_MMAP;
    954   Elf *subelf;
    955   while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
    956     {
    957       Elf_Arhdr *arhdr = elf_getarhdr (subelf);
    958 
    959       /* Ignore the symbol table and the long file name table here.  */
    960       if (strcmp (arhdr->ar_name, "/") == 0
    961 	  || strcmp (arhdr->ar_name, "//") == 0)
    962 	goto next;
    963 
    964       bool do_delete = argc <= 0;
    965       if (!do_delete)
    966 	{
    967 	  ENTRY entry;
    968 	  entry.key = arhdr->ar_name;
    969 	  ENTRY *res = hsearch (entry, FIND);
    970 	  if (res != NULL && (instance < 0 || instance-- == 0)
    971 	      && !found[(char **) res->data - argv])
    972 	    found[(char **) res->data - argv] = do_delete = true;
    973 	}
    974 
    975       if (do_delete)
    976 	{
    977 	  if (verbose)
    978 	    printf ("d - %s\n", arhdr->ar_name);
    979 	}
    980       else
    981 	{
    982 	  struct armem *newp = alloca (sizeof (struct armem));
    983 	  newp->old_off = elf_getaroff (subelf);
    984 	  newp->off = cur_off;
    985 
    986 	  cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1))
    987 		      + sizeof (struct ar_hdr));
    988 
    989 	  if (to_copy == NULL)
    990 	    to_copy = newp->next = newp;
    991 	  else
    992 	    {
    993 	      newp->next = to_copy->next;
    994 	      to_copy = to_copy->next = newp;
    995 	    }
    996 
    997 	  /* If we recreate the symbol table read the file's symbol
    998 	     table now.  */
    999 	  arlib_add_symbols (subelf, arfname, arhdr->ar_name, newp->off);
   1000 
   1001 	  /* Remember long file names.  */
   1002 	  remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name));
   1003 	}
   1004 
   1005     next:
   1006       cmd = elf_next (subelf);
   1007       if (elf_end (subelf) != 0)
   1008 	error (1, 0, "%s: %s", arfname, elf_errmsg (-1));
   1009     }
   1010 
   1011   arlib_finalize ();
   1012 
   1013   hdestroy ();
   1014 
   1015   /* Create a new, temporary file in the same directory as the
   1016      original file.  */
   1017   char tmpfname[strlen (arfname) + 7];
   1018   strcpy (stpcpy (tmpfname, arfname), "XXXXXX");
   1019   int newfd = mkstemp (tmpfname);
   1020   if (unlikely (newfd == -1))
   1021     goto nonew;
   1022 
   1023   /* Create the header.  */
   1024   if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
   1025     {
   1026       // XXX Use /prof/self/fd/%d ???
   1027     nonew_unlink:
   1028       unlink (tmpfname);
   1029       if (newfd != -1)
   1030 	close (newfd);
   1031     nonew:
   1032       error (0, errno, gettext ("cannot create new file"));
   1033       status = 1;
   1034       goto errout;
   1035     }
   1036 
   1037   /* If the archive is empty that is all we have to do.  */
   1038   if (likely (to_copy != NULL))
   1039     {
   1040       /* Write the symbol table or the long file name table or both.  */
   1041       if (symtab.symsnamelen != 0
   1042 	  && ((write_retry (newfd, symtab.symsoff, symtab.symsofflen)
   1043 	       != (ssize_t) symtab.symsofflen)
   1044 	      || (write_retry (newfd, symtab.symsname, symtab.symsnamelen)
   1045 		  != (ssize_t) symtab.symsnamelen)))
   1046 	goto nonew_unlink;
   1047 
   1048       if (symtab.longnameslen > sizeof (struct ar_hdr)
   1049 	  && (write_retry (newfd, symtab.longnames, symtab.longnameslen)
   1050 	      != (ssize_t) symtab.longnameslen))
   1051 	goto nonew_unlink;
   1052 
   1053       /* NULL-terminate the list of files to copy.  */
   1054       struct armem *last = to_copy;
   1055       to_copy = to_copy->next;
   1056       last->next = NULL;
   1057 
   1058       off_t start = -1;
   1059       off_t len = -1;
   1060 
   1061       do
   1062 	if (write_member (to_copy, &start, &len, elf, cur_off, newfd) != 0)
   1063 	  goto nonew_unlink;
   1064       while ((to_copy = to_copy->next) != NULL);
   1065 
   1066       /* Write the last part.  */
   1067       if (copy_content (elf, newfd, start, len))
   1068 	goto nonew_unlink;
   1069     }
   1070 
   1071   /* Set the mode of the new file to the same values the original file
   1072      has.  */
   1073   if (fchmod (newfd, st.st_mode & ALLPERMS) != 0
   1074       /* Never complain about fchown failing.  */
   1075       || (({asm ("" :: "r" (fchown (newfd, st.st_uid, st.st_gid))); }),
   1076 	  close (newfd) != 0)
   1077       || (newfd = -1, rename (tmpfname, arfname) != 0))
   1078     goto nonew_unlink;
   1079 
   1080  errout:
   1081 #ifdef DEBUG
   1082   elf_end (elf);
   1083 
   1084   arlib_fini ();
   1085 
   1086   close (fd);
   1087 #endif
   1088 
   1089   not_found (argc, argv, found);
   1090 
   1091   return status;
   1092 }
   1093 
   1094 
   1095 static void
   1096 no0print (bool ofmt, char *buf, int bufsize, long int val)
   1097 {
   1098   char tmpbuf[bufsize + 1];
   1099   snprintf (tmpbuf, sizeof (tmpbuf), ofmt ? "%-*lo" : "%-*ld", bufsize, val);
   1100   memcpy (buf, tmpbuf, bufsize);
   1101 }
   1102 
   1103 
   1104 static int
   1105 do_oper_insert (int oper, const char *arfname, char **argv, int argc,
   1106 		const char *member)
   1107 {
   1108   int status = 0;
   1109   Elf *elf;
   1110   struct stat st;
   1111   int fd = open_archive (arfname, O_RDONLY, 0, &elf, &st, oper != oper_move);
   1112 
   1113   /* List of the files we keep.  */
   1114   struct armem *all = NULL;
   1115   struct armem *after_memberelem = NULL;
   1116   struct armem **found = alloca (sizeof (*found) * argc);
   1117   memset (found, '\0', sizeof (*found) * argc);
   1118 
   1119   arlib_init ();
   1120 
   1121   /* Initialize early for no_old case.  */
   1122   off_t cur_off = SARMAG;
   1123 
   1124   if (fd == -1)
   1125     {
   1126       if (!suppress_create_msg)
   1127 	fprintf (stderr, "%s: creating %s\n",
   1128 		 program_invocation_short_name, arfname);
   1129 
   1130       goto no_old;
   1131     }
   1132 
   1133   /* Store the names of all files from the command line in a hash
   1134      table so that we can match it.  Note that when no file name is
   1135      given we are basically doing nothing except recreating the
   1136      index.  */
   1137   if (oper != oper_qappend)
   1138     {
   1139       if (hcreate (2 * argc) == 0)
   1140 	error (EXIT_FAILURE, errno, gettext ("cannot create hash table"));
   1141 
   1142       for (int cnt = 0; cnt < argc; ++cnt)
   1143 	{
   1144 	  ENTRY entry;
   1145 	  entry.key = full_path ? argv[cnt] : basename (argv[cnt]);
   1146 	  entry.data = &argv[cnt];
   1147 	  if (hsearch (entry, ENTER) == NULL)
   1148 	    error (EXIT_FAILURE, errno,
   1149 		   gettext ("cannot insert into hash table"));
   1150 	}
   1151     }
   1152 
   1153   /* While iterating over the current content of the archive we must
   1154      determine a number of things: which archive members to keep,
   1155      which are replaced, and where to insert the new members.  */
   1156   Elf_Cmd cmd = ELF_C_READ_MMAP;
   1157   Elf *subelf;
   1158   while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
   1159     {
   1160       Elf_Arhdr *arhdr = elf_getarhdr (subelf);
   1161 
   1162       /* Ignore the symbol table and the long file name table here.  */
   1163       if (strcmp (arhdr->ar_name, "/") == 0
   1164 	  || strcmp (arhdr->ar_name, "//") == 0)
   1165 	goto next;
   1166 
   1167       struct armem *newp = alloca (sizeof (struct armem));
   1168       newp->old_off = elf_getaroff (subelf);
   1169       newp->size = arhdr->ar_size;
   1170       newp->sec = arhdr->ar_date;
   1171       newp->mem = NULL;
   1172 
   1173       /* Remember long file names.  */
   1174       remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name));
   1175 
   1176       /* Check whether this is a file we are looking for.  */
   1177       if (oper != oper_qappend)
   1178 	{
   1179 	  /* Check whether this is the member used as the insert point.  */
   1180 	  if (member != NULL && strcmp (arhdr->ar_name, member) == 0)
   1181 	    {
   1182 	      /* Note that all == NULL means insert at the beginning.  */
   1183 	      if (ipos == ipos_before)
   1184 		after_memberelem = all;
   1185 	      else
   1186 		after_memberelem = newp;
   1187 	      member = NULL;
   1188 	    }
   1189 
   1190 	  ENTRY entry;
   1191 	  entry.key = arhdr->ar_name;
   1192 	  ENTRY *res = hsearch (entry, FIND);
   1193 	  if (res != NULL && found[(char **) res->data - argv] == NULL)
   1194 	    {
   1195 	      found[(char **) res->data - argv] = newp;
   1196 
   1197 	      /* If we insert before or after a certain element move
   1198 		 all files to a special list.  */
   1199 	      if (unlikely (ipos != ipos_none || oper == oper_move))
   1200 		{
   1201 		  if (after_memberelem == newp)
   1202 		    /* Since we remove this element even though we should
   1203 		       insert everything after it, we in fact insert
   1204 		       everything after the previous element.  */
   1205 		    after_memberelem = all;
   1206 
   1207 		  goto next;
   1208 		}
   1209 	    }
   1210 	}
   1211 
   1212       if (all == NULL)
   1213 	all = newp->next = newp;
   1214       else
   1215 	{
   1216 	  newp->next = all->next;
   1217 	  all = all->next = newp;
   1218 	}
   1219 
   1220     next:
   1221       cmd = elf_next (subelf);
   1222       if (elf_end (subelf) != 0)
   1223 	error (EXIT_FAILURE, 0, "%s: %s", arfname, elf_errmsg (-1));
   1224     }
   1225 
   1226   if (oper != oper_qappend)
   1227     hdestroy ();
   1228 
   1229  no_old:
   1230   if (member != NULL)
   1231     error (EXIT_FAILURE, 0, gettext ("position member %s not found"),
   1232 	   member);
   1233 
   1234   if (oper == oper_move)
   1235     {
   1236       /* Make sure all requested elements are found in the archive.  */
   1237       for (int cnt = 0; cnt < argc; ++cnt)
   1238 	{
   1239 	  if (found[cnt] == NULL)
   1240 	    {
   1241 	      fprintf (stderr, gettext ("%s: no entry %s in archive!\n"),
   1242 		       program_invocation_short_name, argv[cnt]);
   1243 	      status = 1;
   1244 	    }
   1245 
   1246 	  if (verbose)
   1247 	    printf ("m - %s\n", argv[cnt]);
   1248 	}
   1249     }
   1250   else
   1251     {
   1252       /* Open all the new files, get their sizes and add all symbols.  */
   1253       for (int cnt = 0; cnt < argc; ++cnt)
   1254 	{
   1255 	  const char *bname = basename (argv[cnt]);
   1256 	  size_t bnamelen = strlen (bname);
   1257 	  if (found[cnt] == NULL)
   1258 	    {
   1259 	      found[cnt] = alloca (sizeof (struct armem));
   1260 	      found[cnt]->old_off = -1;
   1261 
   1262 	      remember_long_name (found[cnt], bname, bnamelen);
   1263 	    }
   1264 
   1265 	  struct stat newst;
   1266 	  Elf *newelf;
   1267 	  int newfd = open (argv[cnt], O_RDONLY);
   1268 	  if (newfd == -1)
   1269 	    {
   1270 	      error (0, errno, gettext ("cannot open %s"), argv[cnt]);
   1271 	      status = 1;
   1272 	    }
   1273 	  else if (fstat (newfd, &newst) == -1)
   1274 	    {
   1275 	      error (0, errno, gettext ("cannot stat %s"), argv[cnt]);
   1276 	      close (newfd);
   1277 	      status = 1;
   1278 	    }
   1279 	  else if (!S_ISREG (newst.st_mode))
   1280 	    {
   1281 	      error (0, errno, gettext ("%s is no regular file"), argv[cnt]);
   1282 	      close (newfd);
   1283 	      status = 1;
   1284 	    }
   1285 	  else if (update_newer
   1286 		   && found[cnt]->old_off != -1l
   1287 		   && found[cnt]->sec > st.st_mtime)
   1288 	    /* Do nothing, the file in the archive is younger.  */
   1289 	    close (newfd);
   1290 	  else if ((newelf = elf_begin (newfd, ELF_C_READ_MMAP, NULL))
   1291 		   == NULL)
   1292 	    {
   1293 	      fprintf (stderr,
   1294 		       gettext ("cannot get ELF descriptor for %s: %s\n"),
   1295 		       argv[cnt], elf_errmsg (-1));
   1296 	      status = 1;
   1297 	    }
   1298 	  else
   1299 	    {
   1300 	      if (verbose)
   1301 		printf ("%c - %s\n",
   1302 			found[cnt]->old_off == -1l ? 'a' : 'r', argv[cnt]);
   1303 
   1304 	      found[cnt]->elf = newelf;
   1305 	      found[cnt]->sec = arlib_deterministic_output ? 0 : newst.st_mtime;
   1306 	      found[cnt]->uid = arlib_deterministic_output ? 0 : newst.st_uid;
   1307 	      found[cnt]->gid = arlib_deterministic_output ? 0 : newst.st_gid;
   1308 	      found[cnt]->mode = newst.st_mode;
   1309 	      found[cnt]->name = bname;
   1310 
   1311 	      found[cnt]->mem = elf_rawfile (newelf, &found[cnt]->size);
   1312 	      if (found[cnt]->mem == NULL
   1313 		  || elf_cntl (newelf, ELF_C_FDDONE) != 0)
   1314 		error (EXIT_FAILURE, 0, gettext ("cannot read %s: %s"),
   1315 		       argv[cnt], elf_errmsg (-1));
   1316 
   1317 	      close (newfd);
   1318 
   1319 	      if (found[cnt]->old_off != -1l)
   1320 		/* Remember long file names.  */
   1321 		remember_long_name (found[cnt], bname, bnamelen);
   1322 	    }
   1323 	}
   1324     }
   1325 
   1326   if (status != 0)
   1327     {
   1328 #ifdef DEBUG
   1329       elf_end (elf);
   1330 
   1331       arlib_fini ();
   1332 
   1333       close (fd);
   1334 #endif
   1335 
   1336       return status;
   1337     }
   1338 
   1339   /* If we have no entry point so far add at the end.  AFTER_MEMBERELEM
   1340      being NULL when adding before an entry means add at the beginning.  */
   1341   if (ipos != ipos_before && after_memberelem == NULL)
   1342     after_memberelem = all;
   1343 
   1344   /* Convert the circular list into a normal list first.  */
   1345   if (all != NULL)
   1346     {
   1347       struct armem *tmp = all;
   1348       all = all->next;
   1349       tmp->next = NULL;
   1350     }
   1351 
   1352   struct armem *last_added = after_memberelem;
   1353   for (int cnt = 0; cnt < argc; ++cnt)
   1354     if (oper != oper_replace || found[cnt]->old_off == -1)
   1355       {
   1356 	if (last_added == NULL)
   1357 	  {
   1358 	    found[cnt]->next = all;
   1359 	    last_added = all = found[cnt];
   1360 	  }
   1361 	else
   1362 	  {
   1363 	    found[cnt]->next = last_added->next;
   1364 	    last_added = last_added->next = found[cnt];
   1365 	  }
   1366       }
   1367 
   1368   /* Finally compute the offset and add the symbols for the files
   1369      after the insert point.  */
   1370   if (likely (all != NULL))
   1371     for (struct armem *memp = all; memp != NULL; memp = memp->next)
   1372       {
   1373 	memp->off = cur_off;
   1374 
   1375 	if (memp->mem == NULL)
   1376 	  {
   1377 	    Elf_Arhdr *arhdr;
   1378 	    /* Fake initializing arhdr and subelf to keep gcc calm.  */
   1379 	    asm ("" : "=m" (arhdr), "=m" (subelf));
   1380 	    if (elf_rand (elf, memp->old_off) == 0
   1381 		|| (subelf = elf_begin (fd, ELF_C_READ_MMAP, elf)) == NULL
   1382 		|| (arhdr = elf_getarhdr (subelf)) == NULL)
   1383 	      /* This should never happen since we already looked at the
   1384 		 archive content.  But who knows...  */
   1385 	      error (EXIT_FAILURE, 0, "%s: %s", arfname, elf_errmsg (-1));
   1386 
   1387 	    arlib_add_symbols (subelf, arfname, arhdr->ar_name, cur_off);
   1388 
   1389 	    elf_end (subelf);
   1390 	  }
   1391 	else
   1392 	  arlib_add_symbols (memp->elf, arfname, memp->name, cur_off);
   1393 
   1394 	cur_off += (((memp->size + 1) & ~((off_t) 1))
   1395 		    + sizeof (struct ar_hdr));
   1396       }
   1397 
   1398   /* Now we have all the information for the symbol table and long
   1399      file name table.  Construct the final layout.  */
   1400   arlib_finalize ();
   1401 
   1402   /* Create a new, temporary file in the same directory as the
   1403      original file.  */
   1404   char tmpfname[strlen (arfname) + 7];
   1405   strcpy (stpcpy (tmpfname, arfname), "XXXXXX");
   1406   int newfd;
   1407   if (fd != -1)
   1408     newfd = mkstemp (tmpfname);
   1409   else
   1410     {
   1411       newfd = open (arfname, O_RDWR | O_CREAT | O_EXCL, DEFFILEMODE);
   1412       if (newfd == -1 && errno == EEXIST)
   1413 	/* Bah, first the file did not exist, now it does.  Restart.  */
   1414 	return do_oper_insert (oper, arfname, argv, argc, member);
   1415     }
   1416   if (unlikely (newfd == -1))
   1417     goto nonew;
   1418 
   1419   /* Create the header.  */
   1420   if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
   1421     {
   1422     nonew_unlink:
   1423       if (fd != -1)
   1424 	{
   1425 	  // XXX Use /prof/self/fd/%d ???
   1426 	  unlink (tmpfname);
   1427 	  if (newfd != -1)
   1428 	    close (newfd);
   1429 	}
   1430     nonew:
   1431       error (0, errno, gettext ("cannot create new file"));
   1432       status = 1;
   1433       goto errout;
   1434     }
   1435 
   1436   /* If the new archive is not empty we actually have something to do.  */
   1437   if (likely (all != NULL))
   1438     {
   1439       /* Write the symbol table or the long file name table or both.  */
   1440       if (symtab.symsnamelen != 0
   1441 	  && ((write_retry (newfd, symtab.symsoff, symtab.symsofflen)
   1442 	       != (ssize_t) symtab.symsofflen)
   1443 	      || (write_retry (newfd, symtab.symsname, symtab.symsnamelen)
   1444 		  != (ssize_t) symtab.symsnamelen)))
   1445 	goto nonew_unlink;
   1446 
   1447       if (symtab.longnameslen > sizeof (struct ar_hdr)
   1448 	  && (write_retry (newfd, symtab.longnames, symtab.longnameslen)
   1449 	      != (ssize_t) symtab.longnameslen))
   1450 	goto nonew_unlink;
   1451 
   1452       off_t start = -1;
   1453       off_t len = -1;
   1454 
   1455       while (all != NULL)
   1456 	{
   1457 	  if (all->mem != NULL)
   1458 	    {
   1459 	      /* This is a new file.  If there is anything from the
   1460 		 archive left to be written do it now.  */
   1461 	      if (start != -1  && copy_content (elf, newfd, start, len))
   1462 		goto nonew_unlink;
   1463 
   1464 	      start = -1;
   1465 	      len = -1;
   1466 
   1467 	      /* Create the header.  */
   1468 	      struct ar_hdr arhdr;
   1469 	      char tmpbuf[sizeof (arhdr.ar_name) + 1];
   1470 	      if (all->long_name_off == -1)
   1471 		{
   1472 		  size_t namelen = strlen (all->name);
   1473 		  char *p = mempcpy (arhdr.ar_name, all->name, namelen);
   1474 		  *p++ = '/';
   1475 		  memset (p, ' ', sizeof (arhdr.ar_name) - namelen - 1);
   1476 		}
   1477 	      else
   1478 		{
   1479 		  snprintf (tmpbuf, sizeof (arhdr.ar_name) + 1, "/%-*ld",
   1480 			    (int) sizeof (arhdr.ar_name), all->long_name_off);
   1481 		  memcpy (arhdr.ar_name, tmpbuf, sizeof (arhdr.ar_name));
   1482 		}
   1483 
   1484 	      no0print (false, arhdr.ar_date, sizeof (arhdr.ar_date),
   1485 			all->sec);
   1486 	      no0print (false, arhdr.ar_uid, sizeof (arhdr.ar_uid), all->uid);
   1487 	      no0print (false, arhdr.ar_gid, sizeof (arhdr.ar_gid), all->gid);
   1488 	      no0print (true, arhdr.ar_mode, sizeof (arhdr.ar_mode),
   1489 			all->mode);
   1490 	      no0print (false, arhdr.ar_size, sizeof (arhdr.ar_size),
   1491 			all->size);
   1492 	      memcpy (arhdr.ar_fmag, ARFMAG, sizeof (arhdr.ar_fmag));
   1493 
   1494 	      if (unlikely (write_retry (newfd, &arhdr, sizeof (arhdr))
   1495 			    != sizeof (arhdr)))
   1496 		goto nonew_unlink;
   1497 
   1498 	      /* Now the file itself.  */
   1499 	      if (unlikely (write_retry (newfd, all->mem, all->size)
   1500 			    != (off_t) all->size))
   1501 		goto nonew_unlink;
   1502 
   1503 	      /* Pad the file if its size is odd.  */
   1504 	      if ((all->size & 1) != 0)
   1505 		if (unlikely (write_retry (newfd, "\n", 1) != 1))
   1506 		  goto nonew_unlink;
   1507 	    }
   1508 	  else
   1509 	    {
   1510 	      /* This is a member from the archive.  */
   1511 	      if (write_member (all, &start, &len, elf, cur_off, newfd)
   1512 		  != 0)
   1513 		goto nonew_unlink;
   1514 	    }
   1515 
   1516 	  all = all->next;
   1517 	}
   1518 
   1519       /* Write the last part.  */
   1520       if (start != -1 && copy_content (elf, newfd, start, len))
   1521 	goto nonew_unlink;
   1522     }
   1523 
   1524   /* Set the mode of the new file to the same values the original file
   1525      has.  */
   1526   if (fd != -1
   1527       && (fchmod (newfd, st.st_mode & ALLPERMS) != 0
   1528 	  /* Never complain about fchown failing.  */
   1529 	  || (({asm ("" :: "r" (fchown (newfd, st.st_uid, st.st_gid))); }),
   1530 	      close (newfd) != 0)
   1531 	  || (newfd = -1, rename (tmpfname, arfname) != 0)))
   1532       goto nonew_unlink;
   1533 
   1534  errout:
   1535 #ifdef DEBUG
   1536   elf_end (elf);
   1537 
   1538   arlib_fini ();
   1539 
   1540   close (fd);
   1541 #endif
   1542 
   1543   return status;
   1544 }
   1545 
   1546 
   1547 #include "debugpred.h"
   1548