• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Create, modify, and extract from archives.
2    Copyright (C) 2005-2012 Red Hat, Inc.
3    Written by Ulrich Drepper <drepper@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
main(int argc,char * argv[])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
print_version(FILE * stream,struct argp_state * state)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
parse_opt(int key,char * arg,struct argp_state * state)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
open_archive(const char * arfname,int flags,int mode,Elf ** elf,struct stat * st,bool miss_allowed)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
not_found(int argc,char * argv[argc],bool found[argc])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
copy_content(Elf * elf,int newfd,off_t off,size_t n)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
do_oper_extract(int oper,const char * arfname,char ** argv,int argc,long int instance)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
write_member(struct armem * memb,off_t * startp,off_t * lenp,Elf * elf,off_t end_off,int newfd)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
remember_long_name(struct armem * mem,const char * name,size_t namelen)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
do_oper_delete(const char * arfname,char ** argv,int argc,long int instance)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
no0print(bool ofmt,char * buf,int bufsize,long int val)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
do_oper_insert(int oper,const char * arfname,char ** argv,int argc,const char * member)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