blob: 149d116dfca84d4d1300ddcd31d3669142e874fe [file] [log] [blame]
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001/* Create, modify, and extract from archives.
Roland McGrathb337b1f2010-01-15 01:02:27 -08002 Copyright (C) 2005-2010 Red Hat, Inc.
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00003 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>
Roland McGrathf82a1ec2007-02-06 04:47:44 +000036#include <limits.h>
Ulrich Drepperce0bdb62007-02-05 07:13:52 +000037#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>
Roland McGrathb337b1f2010-01-15 01:02:27 -080048#include <sys/stat.h>
Ulrich Drepperce0bdb62007-02-05 07:13:52 +000049#include <sys/time.h>
50
51#include <system.h>
52
53#include "arlib.h"
54
55
56/* Name and version of program. */
57static void print_version (FILE *stream, struct argp_state *state);
Ulrich Drepperfdc93e12009-01-17 11:47:10 -080058ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
59
Ulrich Drepperce0bdb62007-02-05 07:13:52 +000060/* Prototypes for local functions. */
61static int do_oper_extract (int oper, const char *arfname, char **argv,
62 int argc, long int instance);
63static int do_oper_delete (const char *arfname, char **argv, int argc,
64 long int instance);
65static int do_oper_insert (int oper, const char *arfname, char **argv,
66 int argc, const char *member);
67
68
69/* Bug report address. */
Ulrich Drepperfdc93e12009-01-17 11:47:10 -080070ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
Ulrich Drepperce0bdb62007-02-05 07:13:52 +000071
72
73/* Definitions of arguments for argp functions. */
74static const struct argp_option options[] =
75{
76 { NULL, 0, NULL, 0, N_("Commands:"), 0 },
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:"), 0 },
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. */
107static const char doc[] = N_("Create, modify, and extract from archives.");
108
109/* Strings for arguments in help texts. */
110static const char args_doc[] = N_("[MEMBER] [COUNT] ARCHIVE [FILE...]");
111
112/* Prototype for option handler. */
113static error_t parse_opt (int key, char *arg, struct argp_state *state);
114
115/* Data structure to communicate with argp functions. */
116static struct argp argp =
117{
118 options, parse_opt, args_doc, doc, NULL, NULL, NULL
119};
120
121
122/* What operation to perform. */
123static 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. */
136static bool verbose;
137static bool preserve_dates;
138static bool instance_specifed;
139static bool dont_replace_existing;
140static bool allow_truncate_fname;
141static bool force_symtab;
142static bool suppress_create_msg;
143static bool full_path;
144static bool update_newer;
145static enum { ipos_none, ipos_before, ipos_after } ipos;
146
147
148int
149main (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. */
Ulrich Drepperb0243862007-06-06 00:09:36 +0000163 (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000164
165 /* Initialize the message catalog. */
Ulrich Drepperb0243862007-06-06 00:09:36 +0000166 (void) textdomain (PACKAGE_TARNAME);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000167
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. */
Ulrich Drepperc54453b2009-02-01 16:19:50 -0800191 if (operation != oper_move && operation != oper_replace)
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000192 error (1, 0, gettext ("\
193'a', 'b', and 'i' are only allowed with the 'm' and 'r' options"));
194
195 if (remaining == argc)
196 {
Ulrich Drepperc54453b2009-02-01 16:19:50 -0800197 error (0, 0, gettext ("\
198MEMBER parameter required for 'a', 'b', and 'i' modifiers"));
199 argp_help (&argp, stderr, ARGP_HELP_USAGE | ARGP_HELP_SEE,
200 program_invocation_short_name);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000201 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"));
Ulrich Drepperc54453b2009-02-01 16:19:50 -0800219 argp_help (&argp, stderr, ARGP_HELP_SEE,
220 program_invocation_short_name);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000221 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))
Roland McGrathfb33c672009-09-08 00:21:18 -0700237 error (1, 0, gettext ("'%c' is only meaningful with the 'x' option"),
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000238 dont_replace_existing ? 'C' : 'T');
239
240 /* There must at least be one more parameter specifying the archive. */
241 if (remaining == argc)
242 {
Ulrich Drepperc54453b2009-02-01 16:19:50 -0800243 error (0, 0, gettext ("archive name required"));
244 argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000245 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_list:
256 case oper_print:
257 status = do_oper_extract (operation, arfname, argv, argc, -1);
258 break;
259
260 case oper_extract:
261 status = do_oper_extract (operation, arfname, argv, argc, instance);
262 break;
263
264 case oper_delete:
265 status = do_oper_delete (arfname, argv, argc, instance);
266 break;
267
268 case oper_move:
269 case oper_qappend:
270 case oper_replace:
271 status = do_oper_insert (operation, arfname, argv, argc, member);
272 break;
273
274 default:
275 assert (! "should not happen");
276 status = 1;
277 break;
278 }
279
280 return status;
281}
282
283
284/* Print the version information. */
285static void
286print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
287{
Ulrich Drepperb0243862007-06-06 00:09:36 +0000288 fprintf (stream, "ar (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000289 fprintf (stream, gettext ("\
290Copyright (C) %s Red Hat, Inc.\n\
291This is free software; see the source for copying conditions. There is NO\n\
292warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
Ulrich Drepper44173ed2009-01-01 19:00:41 -0800293"), "2009");
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000294 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
295}
296
297
298/* Handle program arguments. */
299static error_t
300parse_opt (int key, char *arg __attribute__ ((unused)),
301 struct argp_state *state __attribute__ ((unused)))
302{
303 switch (key)
304 {
305 case 'd':
306 case 'm':
307 case 'p':
308 case 'q':
309 case 'r':
310 case 't':
311 case 'x':
312 if (operation != oper_none)
313 {
314 error (0, 0, gettext ("More than one operation specified"));
Ulrich Drepperc54453b2009-02-01 16:19:50 -0800315 argp_help (&argp, stderr, ARGP_HELP_SEE,
316 program_invocation_short_name);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000317 exit (EXIT_FAILURE);
318 }
319
320 switch (key)
321 {
322 case 'd':
323 operation = oper_delete;
324 break;
325 case 'm':
326 operation = oper_move;
327 break;
328 case 'p':
329 operation = oper_print;
330 break;
331 case 'q':
332 operation = oper_qappend;
333 break;
334 case 'r':
335 operation = oper_replace;
336 break;
337 case 't':
338 operation = oper_list;
339 break;
340 case 'x':
341 operation = oper_extract;
342 break;
343 }
344 break;
345
346 case 'a':
347 ipos = ipos_after;
348 break;
349
350 case 'b':
351 case 'i':
352 ipos = ipos_before;
353 break;
354
355 case 'c':
356 suppress_create_msg = true;
357 break;
358
359 case 'C':
360 dont_replace_existing = true;
361 break;
362
363 case 'N':
364 instance_specifed = true;
365 break;
366
367 case 'o':
368 preserve_dates = true;
369 break;
370
371 case 'P':
372 full_path = true;
373 break;
374
375 case 's':
376 force_symtab = true;
377 break;
378
379 case 'T':
380 allow_truncate_fname = true;
381 break;
382
383 case 'v':
384 verbose = true;
385 break;
386
387 default:
388 return ARGP_ERR_UNKNOWN;
389 }
390 return 0;
391}
392
393
394static int
395open_archive (const char *arfname, int flags, int mode, Elf **elf,
396 struct stat *st, bool miss_allowed)
397{
398 int fd = open (arfname, flags, mode);
399 if (fd == -1)
400 {
401 if (miss_allowed)
402 return -1;
403
404 error (EXIT_FAILURE, errno, gettext ("cannot open archive '%s'"),
405 arfname);
406 }
407
408 if (elf != NULL)
409 {
410 Elf_Cmd cmd = flags == O_RDONLY ? ELF_C_READ_MMAP : ELF_C_RDWR_MMAP;
411
412 *elf = elf_begin (fd, cmd, NULL);
413 if (*elf == NULL)
414 error (EXIT_FAILURE, 0, gettext ("cannot open archive '%s': %s"),
415 arfname, elf_errmsg (-1));
416
417 if (flags == O_RDONLY && elf_kind (*elf) != ELF_K_AR)
418 error (EXIT_FAILURE, 0, gettext ("%s: not an archive file"), arfname);
419 }
420
421 if (st != NULL && fstat (fd, st) != 0)
422 error (EXIT_FAILURE, errno, gettext ("cannot stat archive '%s'"),
423 arfname);
424
425 return fd;
426}
427
428
429static void
430not_found (int argc, char *argv[argc], bool found[argc])
431{
432 for (int i = 0; i < argc; ++i)
433 if (!found[i])
434 printf (gettext ("no entry %s in archive\n"), argv[i]);
435}
436
437
438static int
439copy_content (Elf *elf, int newfd, off_t off, size_t n)
440{
441 size_t len;
442 char *rawfile = elf_rawfile (elf, &len);
443
444 assert (off + n <= len);
445
446 /* Tell the kernel we will read all the pages sequentially. */
447 size_t ps = sysconf (_SC_PAGESIZE);
448 if (n > 2 * ps)
449 posix_madvise (rawfile + (off & ~(ps - 1)), n, POSIX_MADV_SEQUENTIAL);
450
451 return write_retry (newfd, rawfile + off, n) != (ssize_t) n;
452}
453
454
455static int
456do_oper_extract (int oper, const char *arfname, char **argv, int argc,
457 long int instance)
458{
459 bool found[argc];
460 memset (found, '\0', sizeof (found));
461
Roland McGrath536127f2009-08-14 13:10:09 -0700462 size_t name_max = 0;
463 inline bool should_truncate_fname (void)
464 {
465 if (errno == ENAMETOOLONG && allow_truncate_fname)
466 {
467 if (name_max == 0)
468 {
469 long int len = pathconf (".", _PC_NAME_MAX);
470 if (len > 0)
471 name_max = len;
472 }
473 return name_max != 0;
474 }
475 return false;
476 }
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000477
478 off_t index_off = -1;
479 size_t index_size = 0;
480 off_t cur_off = SARMAG;
481
482 int status = 0;
483 Elf *elf;
484 int fd = open_archive (arfname, O_RDONLY, 0, &elf, NULL, false);
485
486 if (hcreate (2 * argc) == 0)
487 error (EXIT_FAILURE, errno, gettext ("cannot create hash table"));
488
489 for (int cnt = 0; cnt < argc; ++cnt)
490 {
491 ENTRY entry = { .key = argv[cnt], .data = &argv[cnt] };
492 if (hsearch (entry, ENTER) == NULL)
493 error (EXIT_FAILURE, errno,
494 gettext ("cannot insert into hash table"));
495 }
496
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000497 struct stat st;
498 if (force_symtab)
499 {
500 if (fstat (fd, &st) != 0)
501 {
502 error (0, errno, gettext ("cannot stat '%s'"), arfname);
503 close (fd);
504 return 1;
505 }
Ulrich Drepper0fe63532007-02-05 21:05:51 +0000506 arlib_init ();
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000507 }
508
509 Elf_Cmd cmd = ELF_C_READ_MMAP;
510 Elf *subelf;
511 while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
512 {
513 Elf_Arhdr *arhdr = elf_getarhdr (subelf);
514
515 if (strcmp (arhdr->ar_name, "/") == 0)
516 {
517 index_off = elf_getaroff (subelf);
518 index_size = arhdr->ar_size;
519 goto next;
520 }
521 if (strcmp (arhdr->ar_name, "//") == 0)
522 goto next;
523
524 if (force_symtab)
525 {
Ulrich Drepper0fe63532007-02-05 21:05:51 +0000526 arlib_add_symbols (elf, arfname, arhdr->ar_name, cur_off);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000527 cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1))
528 + sizeof (struct ar_hdr));
529 }
530
531 bool do_extract = argc <= 0;
532 if (!do_extract)
533 {
534 ENTRY entry;
535 entry.key = arhdr->ar_name;
536 ENTRY *res = hsearch (entry, FIND);
537 if (res != NULL && (instance < 0 || instance-- == 0)
538 && !found[(char **) res->data - argv])
539 found[(char **) res->data - argv] = do_extract = true;
540 }
541
542 if (do_extract)
543 {
544 if (verbose)
545 {
546 if (oper == oper_print)
547 {
548 printf ("\n<%s>\n\n", arhdr->ar_name);
549
550 /* We have to flush now because now we use the descriptor
551 directly. */
552 fflush (stdout);
553 }
554 else if (oper == oper_list)
555 {
556 char datestr[100];
557 strftime (datestr, sizeof (datestr), "%b %e %H:%M %Y",
558 localtime (&arhdr->ar_date));
559
560 printf ("%c%c%c%c%c%c%c%c%c %u/%u %6ju %s %s\n",
561 (arhdr->ar_mode & S_IRUSR) ? 'r' : '-',
562 (arhdr->ar_mode & S_IWUSR) ? 'w' : '-',
563 (arhdr->ar_mode & S_IXUSR)
564 ? ((arhdr->ar_mode & S_ISUID) ? 's' : 'x')
565 : ((arhdr->ar_mode & S_ISUID) ? 'S' : '-'),
566 (arhdr->ar_mode & S_IRGRP) ? 'r' : '-',
567 (arhdr->ar_mode & S_IWGRP) ? 'w' : '-',
568 (arhdr->ar_mode & S_IXGRP)
569 ? ((arhdr->ar_mode & S_ISGID) ? 's' : 'x')
570 : ((arhdr->ar_mode & S_ISGID) ? 'S' : '-'),
571 (arhdr->ar_mode & S_IROTH) ? 'r' : '-',
572 (arhdr->ar_mode & S_IWOTH) ? 'w' : '-',
573 (arhdr->ar_mode & S_IXOTH)
574 ? ((arhdr->ar_mode & S_ISVTX) ? 't' : 'x')
575 : ((arhdr->ar_mode & S_ISVTX) ? 'T' : '-'),
576 arhdr->ar_uid,
577 arhdr->ar_gid,
578 (uintmax_t) arhdr->ar_size,
579 datestr,
580 arhdr->ar_name);
581 }
582 else
583 printf ("x - %s\n", arhdr->ar_name);
584 }
585
586 if (oper == oper_list)
587 {
588 if (!verbose)
589 puts (arhdr->ar_name);
590
591 goto next;
592 }
593
594 size_t nleft;
595 char *data = elf_rawfile (subelf, &nleft);
596 if (data == NULL)
597 {
598 error (0, 0, gettext ("cannot read content of %s: %s"),
599 arhdr->ar_name, elf_errmsg (-1));
600 status = 1;
601 goto next;
602 }
603
604 int xfd;
605 char tempfname[] = "XXXXXX";
606 bool use_mkstemp = true;
607
608 if (oper == oper_print)
609 xfd = STDOUT_FILENO;
610 else
611 {
612 xfd = mkstemp (tempfname);
613 if (unlikely (xfd == -1))
614 {
615 /* We cannot create a temporary file. Try to overwrite
616 the file or create it if it does not exist. */
617 int flags = O_WRONLY | O_CREAT;
618 if (dont_replace_existing)
619 flags |= O_EXCL;
620 else
621 flags |= O_TRUNC;
622 xfd = open (arhdr->ar_name, flags, 0600);
623 if (unlikely (xfd == -1))
624 {
625 int printlen = INT_MAX;
626
Roland McGrath536127f2009-08-14 13:10:09 -0700627 if (should_truncate_fname ())
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000628 {
629 /* Try to truncate the name. First find out by how
630 much. */
Roland McGrath536127f2009-08-14 13:10:09 -0700631 printlen = name_max;
632 char truncfname[name_max + 1];
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000633 *((char *) mempcpy (truncfname, arhdr->ar_name,
Roland McGrath536127f2009-08-14 13:10:09 -0700634 name_max)) = '\0';
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000635
636 xfd = open (truncfname, flags, 0600);
637 }
638
639 if (xfd == -1)
640 {
641 error (0, errno, gettext ("cannot open %.*s"),
642 (int) printlen, arhdr->ar_name);
643 status = 1;
644 goto next;
645 }
646 }
647
648 use_mkstemp = false;
649 }
650 }
651
652 ssize_t n;
653 while ((n = TEMP_FAILURE_RETRY (write (xfd, data, nleft))) != -1)
654 {
655 nleft -= n;
656 if (nleft == 0)
657 break;
658 data += n;
659 }
660
661 if (unlikely (n == -1))
662 {
663 error (0, errno, gettext ("failed to write %s"), arhdr->ar_name);
664 status = 1;
665 unlink (tempfname);
666 close (xfd);
667 goto next;
668 }
669
670 if (oper != oper_print)
671 {
672 /* Fix up the mode. */
673 if (unlikely (fchmod (xfd, arhdr->ar_mode) != 0))
674 {
675 error (0, errno, gettext ("cannot change mode of %s"),
676 arhdr->ar_name);
677 status = 0;
678 }
679
680 if (preserve_dates)
681 {
682 struct timeval tv[2];
683 tv[0].tv_sec = arhdr->ar_date;
684 tv[0].tv_usec = 0;
685 tv[1].tv_sec = arhdr->ar_date;
686 tv[1].tv_usec = 0;
687
688 if (unlikely (futimes (xfd, tv) != 0))
689 {
690 error (0, errno,
691 gettext ("cannot change modification time of %s"),
692 arhdr->ar_name);
693 status = 1;
694 }
695 }
696
697 /* If we used a temporary file, move it do the right
698 name now. */
699 if (use_mkstemp)
700 {
701 int r;
702
703 if (dont_replace_existing)
704 {
705 r = link (tempfname, arhdr->ar_name);
706 if (likely (r == 0))
707 unlink (tempfname);
708 }
709 else
710 r = rename (tempfname, arhdr->ar_name);
711
712 if (unlikely (r) != 0)
713 {
714 int printlen = INT_MAX;
715
Roland McGrath536127f2009-08-14 13:10:09 -0700716 if (should_truncate_fname ())
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000717 {
718 /* Try to truncate the name. First find out by how
719 much. */
Roland McGrath536127f2009-08-14 13:10:09 -0700720 printlen = name_max;
721 char truncfname[name_max + 1];
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000722 *((char *) mempcpy (truncfname, arhdr->ar_name,
Roland McGrath536127f2009-08-14 13:10:09 -0700723 name_max)) = '\0';
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000724
725 if (dont_replace_existing)
726 {
727 r = link (tempfname, truncfname);
728 if (likely (r == 0))
729 unlink (tempfname);
730 }
731 else
732 r = rename (tempfname, truncfname);
733 }
734
735 if (r != 0)
736 {
737 error (0, errno, gettext ("\
738cannot rename temporary file to %.*s"),
739 printlen, arhdr->ar_name);
740 unlink (tempfname);
741 status = 1;
742 }
743 }
744 }
745
746 close (xfd);
747 }
748 }
749
750 next:
751 cmd = elf_next (subelf);
752 if (elf_end (subelf) != 0)
753 error (1, 0, "%s: %s", arfname, elf_errmsg (-1));
754 }
755
756 hdestroy ();
757
758 if (force_symtab)
759 {
Ulrich Drepper0fe63532007-02-05 21:05:51 +0000760 arlib_finalize ();
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000761
762 if (symtab.symsnamelen != 0
763 /* We have to rewrite the file also if it initially had an index
764 but now does not need one anymore. */
765 || (symtab.symsnamelen == 0 && index_size != 0))
766 {
767 char tmpfname[strlen (arfname) + 7];
768 strcpy (stpcpy (tmpfname, arfname), "XXXXXX");
769 int newfd = mkstemp (tmpfname);
770 if (unlikely (newfd == -1))
771 {
772 nonew:
773 error (0, errno, gettext ("cannot create new file"));
774 status = 1;
775 }
776 else
777 {
778 /* Create the header. */
779 if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
780 {
781 // XXX Use /prof/self/fd/%d ???
782 nonew_unlink:
783 unlink (tmpfname);
784 if (newfd != -1)
785 close (newfd);
786 goto nonew;
787 }
788
789 /* Create the new file. There are three parts as far we are
790 concerned: 1. original context before the index, 2. the
791 new index, 3. everything after the new index. */
792 off_t rest_off;
793 if (index_off != -1)
794 rest_off = (index_off + sizeof (struct ar_hdr)
795 + ((index_size + 1) & ~1ul));
796 else
797 rest_off = SARMAG;
798
799 if ((symtab.symsnamelen != 0
800 && ((write_retry (newfd, symtab.symsoff,
801 symtab.symsofflen)
802 != (ssize_t) symtab.symsofflen)
803 || (write_retry (newfd, symtab.symsname,
804 symtab.symsnamelen)
805 != (ssize_t) symtab.symsnamelen)))
806 /* Even if the original file had content before the
807 symbol table, we write it in the correct order. */
808 || (index_off != SARMAG
809 && copy_content (elf, newfd, SARMAG, index_off - SARMAG))
810 || copy_content (elf, newfd, rest_off, st.st_size - rest_off)
811 /* Set the mode of the new file to the same values the
812 original file has. */
813 || fchmod (newfd, st.st_mode & ALLPERMS) != 0
814 /* Never complain about fchown failing. */
Ulrich Drepper93ab56f2007-02-05 22:12:41 +0000815 || (({asm ("" :: "r" (fchown (newfd, st.st_uid,
816 st.st_gid))); }),
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000817 close (newfd) != 0)
818 || (newfd = -1, rename (tmpfname, arfname) != 0))
819 goto nonew_unlink;
820 }
821 }
822 }
823
824 elf_end (elf);
825
826 close (fd);
827
828 not_found (argc, argv, found);
829
830 return status;
831}
832
833
834struct armem
835{
836 off_t off;
837 off_t old_off;
838 size_t size;
839 long int long_name_off;
840 struct armem *next;
841 void *mem;
842 time_t sec;
843 uid_t uid;
844 gid_t gid;
845 mode_t mode;
846 const char *name;
847 Elf *elf;
848};
849
850
851static int
852write_member (struct armem *memb, off_t *startp, off_t *lenp, Elf *elf,
853 off_t end_off, int newfd)
854{
855 struct ar_hdr arhdr;
856 char tmpbuf[sizeof (arhdr.ar_name) + 1];
857
858 bool changed_header = memb->long_name_off != -1;
859 if (changed_header)
860 {
861 /* In case of a long file name we assume the archive header
862 changed and we write it here. */
863 memcpy (&arhdr, elf_rawfile (elf, NULL) + *startp, sizeof (arhdr));
864
865 snprintf (tmpbuf, sizeof (tmpbuf), "/%-*ld",
866 (int) sizeof (arhdr.ar_name), memb->long_name_off);
867 changed_header = memcmp (arhdr.ar_name, tmpbuf,
868 sizeof (arhdr.ar_name)) != 0;
869 }
870
871 /* If the files are adjacent in the old file extend the range. */
872 if (*startp != -1 && !changed_header && *startp + *lenp == memb->old_off)
873 {
874 /* Extend the current range. */
875 *lenp += (memb->next != NULL
876 ? memb->next->off : end_off) - memb->off;
877 return 0;
878 }
879
880 /* Write out the old range. */
881 if (*startp != -1 && copy_content (elf, newfd, *startp, *lenp))
882 return -1;
883
884 *startp = memb->old_off;
885 *lenp = (memb->next != NULL ? memb->next->off : end_off) - memb->off;
886
887 if (changed_header)
888 {
889 memcpy (arhdr.ar_name, tmpbuf, sizeof (arhdr.ar_name));
890
891 if (unlikely (write_retry (newfd, &arhdr, sizeof (arhdr))
892 != sizeof (arhdr)))
893 return -1;
894
895 *startp += sizeof (struct ar_hdr);
896 assert ((size_t) *lenp >= sizeof (struct ar_hdr));
897 *lenp -= sizeof (struct ar_hdr);
898 }
899
900 return 0;
901}
902
Ulrich Drepperb597dfa2007-10-16 05:21:27 +0000903/* Store the name in the long name table if necessary.
904 Record its offset or -1 if we did not need to use the table. */
905static void
906remember_long_name (struct armem *mem, const char *name, size_t namelen)
907{
908 mem->long_name_off = (namelen > MAX_AR_NAME_LEN
909 ? arlib_add_long_name (name, namelen)
910 : -1l);
911}
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000912
913static int
914do_oper_delete (const char *arfname, char **argv, int argc,
915 long int instance)
916{
917 bool *found = alloca (sizeof (bool) * argc);
918 memset (found, '\0', sizeof (found));
919
920 /* List of the files we keep. */
921 struct armem *to_copy = NULL;
922
923 int status = 0;
924 Elf *elf;
925 struct stat st;
926 int fd = open_archive (arfname, O_RDONLY, 0, &elf, &st, false);
927
928 if (hcreate (2 * argc) == 0)
929 error (EXIT_FAILURE, errno, gettext ("cannot create hash table"));
930
931 for (int cnt = 0; cnt < argc; ++cnt)
932 {
933 ENTRY entry = { .key = argv[cnt], .data = &argv[cnt] };
934 if (hsearch (entry, ENTER) == NULL)
935 error (EXIT_FAILURE, errno,
936 gettext ("cannot insert into hash table"));
937 }
938
Ulrich Drepper0fe63532007-02-05 21:05:51 +0000939 arlib_init ();
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000940
941 off_t cur_off = SARMAG;
942 Elf_Cmd cmd = ELF_C_READ_MMAP;
943 Elf *subelf;
944 while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
945 {
946 Elf_Arhdr *arhdr = elf_getarhdr (subelf);
947
948 /* Ignore the symbol table and the long file name table here. */
949 if (strcmp (arhdr->ar_name, "/") == 0
950 || strcmp (arhdr->ar_name, "//") == 0)
951 goto next;
952
953 bool do_delete = argc <= 0;
954 if (!do_delete)
955 {
956 ENTRY entry;
957 entry.key = arhdr->ar_name;
958 ENTRY *res = hsearch (entry, FIND);
959 if (res != NULL && (instance < 0 || instance-- == 0)
960 && !found[(char **) res->data - argv])
961 found[(char **) res->data - argv] = do_delete = true;
962 }
963
964 if (do_delete)
965 {
966 if (verbose)
967 printf ("d - %s\n", arhdr->ar_name);
968 }
969 else
970 {
971 struct armem *newp = alloca (sizeof (struct armem));
972 newp->old_off = elf_getaroff (subelf);
973 newp->off = cur_off;
974
975 cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1))
976 + sizeof (struct ar_hdr));
977
978 if (to_copy == NULL)
979 to_copy = newp->next = newp;
980 else
981 {
982 newp->next = to_copy->next;
983 to_copy = to_copy->next = newp;
984 }
985
986 /* If we recreate the symbol table read the file's symbol
987 table now. */
Ulrich Drepper0fe63532007-02-05 21:05:51 +0000988 arlib_add_symbols (subelf, arfname, arhdr->ar_name, newp->off);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000989
990 /* Remember long file names. */
Ulrich Drepperb597dfa2007-10-16 05:21:27 +0000991 remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name));
Ulrich Drepperce0bdb62007-02-05 07:13:52 +0000992 }
993
994 next:
995 cmd = elf_next (subelf);
996 if (elf_end (subelf) != 0)
997 error (1, 0, "%s: %s", arfname, elf_errmsg (-1));
998 }
999
Ulrich Drepper0fe63532007-02-05 21:05:51 +00001000 arlib_finalize ();
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001001
1002 hdestroy ();
1003
1004 /* Create a new, temporary file in the same directory as the
1005 original file. */
1006 char tmpfname[strlen (arfname) + 7];
1007 strcpy (stpcpy (tmpfname, arfname), "XXXXXX");
1008 int newfd = mkstemp (tmpfname);
1009 if (unlikely (newfd == -1))
1010 goto nonew;
1011
1012 /* Create the header. */
1013 if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
1014 {
1015 // XXX Use /prof/self/fd/%d ???
1016 nonew_unlink:
1017 unlink (tmpfname);
1018 if (newfd != -1)
1019 close (newfd);
1020 nonew:
1021 error (0, errno, gettext ("cannot create new file"));
1022 status = 1;
1023 goto errout;
1024 }
1025
1026 /* If the archive is empty that is all we have to do. */
1027 if (likely (to_copy != NULL))
1028 {
1029 /* Write the symbol table or the long file name table or both. */
1030 if (symtab.symsnamelen != 0
1031 && ((write_retry (newfd, symtab.symsoff, symtab.symsofflen)
1032 != (ssize_t) symtab.symsofflen)
1033 || (write_retry (newfd, symtab.symsname, symtab.symsnamelen)
1034 != (ssize_t) symtab.symsnamelen)))
1035 goto nonew_unlink;
1036
1037 if (symtab.longnameslen > sizeof (struct ar_hdr)
1038 && (write_retry (newfd, symtab.longnames, symtab.longnameslen)
1039 != (ssize_t) symtab.longnameslen))
1040 goto nonew_unlink;
1041
1042 /* NULL-terminate the list of files to copy. */
1043 struct armem *last = to_copy;
1044 to_copy = to_copy->next;
1045 last->next = NULL;
1046
1047 off_t start = -1;
1048 off_t len = -1;
1049
1050 do
1051 if (write_member (to_copy, &start, &len, elf, cur_off, newfd) != 0)
1052 goto nonew_unlink;
1053 while ((to_copy = to_copy->next) != NULL);
1054
1055 /* Write the last part. */
1056 if (copy_content (elf, newfd, start, len))
1057 goto nonew_unlink;
1058 }
1059
1060 /* Set the mode of the new file to the same values the original file
1061 has. */
1062 if (fchmod (newfd, st.st_mode & ALLPERMS) != 0
1063 /* Never complain about fchown failing. */
Ulrich Drepper93ab56f2007-02-05 22:12:41 +00001064 || (({asm ("" :: "r" (fchown (newfd, st.st_uid, st.st_gid))); }),
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001065 close (newfd) != 0)
1066 || (newfd = -1, rename (tmpfname, arfname) != 0))
1067 goto nonew_unlink;
1068
1069 errout:
1070#ifdef DEBUG
1071 elf_end (elf);
1072
Ulrich Drepper0fe63532007-02-05 21:05:51 +00001073 arlib_fini ();
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001074
1075 close (fd);
1076#endif
1077
1078 not_found (argc, argv, found);
1079
1080 return status;
1081}
1082
1083
1084static void
1085no0print (bool ofmt, char *buf, int bufsize, long int val)
1086{
1087 char tmpbuf[bufsize + 1];
1088 snprintf (tmpbuf, sizeof (tmpbuf), ofmt ? "%-*lo" : "%-*ld", bufsize, val);
1089 memcpy (buf, tmpbuf, bufsize);
1090}
1091
1092
1093static int
1094do_oper_insert (int oper, const char *arfname, char **argv, int argc,
1095 const char *member)
1096{
1097 int status = 0;
1098 Elf *elf;
1099 struct stat st;
1100 int fd = open_archive (arfname, O_RDONLY, 0, &elf, &st, oper != oper_move);
1101
1102 /* List of the files we keep. */
1103 struct armem *all = NULL;
1104 struct armem *after_memberelem = NULL;
1105 struct armem **found = alloca (sizeof (*found) * argc);
1106 memset (found, '\0', sizeof (*found) * argc);
1107
Ulrich Drepper0fe63532007-02-05 21:05:51 +00001108 arlib_init ();
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001109
Ulrich Drepperb597dfa2007-10-16 05:21:27 +00001110 /* Initialize early for no_old case. */
1111 off_t cur_off = SARMAG;
1112
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001113 if (fd == -1)
1114 {
1115 if (!suppress_create_msg)
Ulrich Drepperc54453b2009-02-01 16:19:50 -08001116 fprintf (stderr, "%s: creating %s\n",
1117 program_invocation_short_name, arfname);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001118
1119 goto no_old;
1120 }
1121
1122 /* Store the names of all files from the command line in a hash
1123 table so that we can match it. Note that when no file name is
1124 given we are basically doing nothing except recreating the
1125 index. */
1126 if (oper != oper_qappend)
1127 {
1128 if (hcreate (2 * argc) == 0)
1129 error (EXIT_FAILURE, errno, gettext ("cannot create hash table"));
1130
1131 for (int cnt = 0; cnt < argc; ++cnt)
1132 {
1133 ENTRY entry;
1134 entry.key = full_path ? argv[cnt] : basename (argv[cnt]);
1135 entry.data = &argv[cnt];
1136 if (hsearch (entry, ENTER) == NULL)
1137 error (EXIT_FAILURE, errno,
1138 gettext ("cannot insert into hash table"));
1139 }
1140 }
1141
1142 /* While iterating over the current content of the archive we must
1143 determine a number of things: which archive members to keep,
1144 which are replaced, and where to insert the new members. */
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001145 Elf_Cmd cmd = ELF_C_READ_MMAP;
1146 Elf *subelf;
1147 while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
1148 {
1149 Elf_Arhdr *arhdr = elf_getarhdr (subelf);
1150
1151 /* Ignore the symbol table and the long file name table here. */
1152 if (strcmp (arhdr->ar_name, "/") == 0
1153 || strcmp (arhdr->ar_name, "//") == 0)
1154 goto next;
1155
1156 struct armem *newp = alloca (sizeof (struct armem));
1157 newp->old_off = elf_getaroff (subelf);
1158 newp->size = arhdr->ar_size;
1159 newp->sec = arhdr->ar_date;
1160 newp->mem = NULL;
1161
1162 /* Remember long file names. */
Ulrich Drepperb597dfa2007-10-16 05:21:27 +00001163 remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name));
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001164
1165 /* Check whether this is a file we are looking for. */
1166 if (oper != oper_qappend)
1167 {
1168 /* Check whether this is the member used as the insert point. */
1169 if (member != NULL && strcmp (arhdr->ar_name, member) == 0)
1170 {
1171 /* Note that all == NULL means insert at the beginning. */
1172 if (ipos == ipos_before)
1173 after_memberelem = all;
1174 else
1175 after_memberelem = newp;
1176 member = NULL;
1177 }
1178
1179 ENTRY entry;
1180 entry.key = arhdr->ar_name;
1181 ENTRY *res = hsearch (entry, FIND);
1182 if (res != NULL && found[(char **) res->data - argv] == NULL)
1183 {
1184 found[(char **) res->data - argv] = newp;
1185
1186 /* If we insert before or after a certain element move
1187 all files to a special list. */
1188 if (unlikely (ipos != ipos_none || oper == oper_move))
1189 {
1190 if (after_memberelem == newp)
1191 /* Since we remove this element even though we should
1192 insert everything after it, we in fact insert
1193 everything after the previous element. */
1194 after_memberelem = all;
1195
1196 goto next;
1197 }
1198 }
1199 }
1200
1201 if (all == NULL)
1202 all = newp->next = newp;
1203 else
1204 {
1205 newp->next = all->next;
1206 all = all->next = newp;
1207 }
1208
1209 next:
1210 cmd = elf_next (subelf);
1211 if (elf_end (subelf) != 0)
1212 error (EXIT_FAILURE, 0, "%s: %s", arfname, elf_errmsg (-1));
1213 }
1214
1215 if (oper != oper_qappend)
1216 hdestroy ();
1217
1218 no_old:
1219 if (member != NULL)
1220 error (EXIT_FAILURE, 0, gettext ("position member %s not found"),
1221 member);
1222
1223 if (oper == oper_move)
1224 {
1225 /* Make sure all requested elements are found in the archive. */
1226 for (int cnt = 0; cnt < argc; ++cnt)
1227 {
1228 if (found[cnt] == NULL)
1229 {
1230 fprintf (stderr, gettext ("%s: no entry %s in archive!\n"),
Ulrich Drepperc54453b2009-02-01 16:19:50 -08001231 program_invocation_short_name, argv[cnt]);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001232 status = 1;
1233 }
1234
1235 if (verbose)
1236 printf ("m - %s\n", argv[cnt]);
1237 }
1238 }
1239 else
1240 {
1241 /* Open all the new files, get their sizes and add all symbols. */
1242 for (int cnt = 0; cnt < argc; ++cnt)
1243 {
1244 const char *bname = basename (argv[cnt]);
Ulrich Drepperb597dfa2007-10-16 05:21:27 +00001245 size_t bnamelen = strlen (bname);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001246 if (found[cnt] == NULL)
1247 {
1248 found[cnt] = alloca (sizeof (struct armem));
1249 found[cnt]->old_off = -1;
1250
Ulrich Drepperb597dfa2007-10-16 05:21:27 +00001251 remember_long_name (found[cnt], bname, bnamelen);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001252 }
1253
1254 struct stat newst;
1255 Elf *newelf;
1256 int newfd = open (argv[cnt], O_RDONLY);
1257 if (newfd == -1)
1258 {
1259 error (0, errno, gettext ("cannot open %s"), argv[cnt]);
1260 status = 1;
1261 }
1262 else if (fstat (newfd, &newst) == -1)
1263 {
1264 error (0, errno, gettext ("cannot stat %s"), argv[cnt]);
1265 close (newfd);
1266 status = 1;
1267 }
1268 else if (!S_ISREG (newst.st_mode))
1269 {
1270 error (0, errno, gettext ("%s is no regular file"), argv[cnt]);
1271 close (newfd);
1272 status = 1;
1273 }
1274 else if (update_newer
1275 && found[cnt]->old_off != -1l
1276 && found[cnt]->sec > st.st_mtime)
1277 /* Do nothing, the file in the archive is younger. */
1278 close (newfd);
1279 else if ((newelf = elf_begin (newfd, ELF_C_READ_MMAP, NULL))
1280 == NULL)
1281 {
1282 fprintf (stderr,
1283 gettext ("cannot get ELF descriptor for %s: %s\n"),
1284 argv[cnt], elf_errmsg (-1));
1285 status = 1;
1286 }
1287 else
1288 {
1289 if (verbose)
1290 printf ("%c - %s\n",
1291 found[cnt]->old_off == -1l ? 'a' : 'r', argv[cnt]);
1292
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001293 found[cnt]->elf = newelf;
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001294 found[cnt]->sec = newst.st_mtime;
1295 found[cnt]->uid = newst.st_uid;
1296 found[cnt]->gid = newst.st_gid;
1297 found[cnt]->mode = newst.st_mode;
Ulrich Drepperb597dfa2007-10-16 05:21:27 +00001298 found[cnt]->name = bname;
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001299
1300 found[cnt]->mem = elf_rawfile (newelf, &found[cnt]->size);
1301 if (found[cnt] == NULL || elf_cntl (newelf, ELF_C_FDDONE) != 0)
1302 error (EXIT_FAILURE, 0, gettext ("cannot read %s: %s"),
1303 argv[cnt], elf_errmsg (-1));
1304
1305 close (newfd);
1306
Ulrich Drepperb597dfa2007-10-16 05:21:27 +00001307 if (found[cnt]->old_off != -1l)
1308 /* Remember long file names. */
1309 remember_long_name (found[cnt], bname, bnamelen);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001310 }
1311 }
1312 }
1313
1314 if (status != 0)
1315 {
1316#ifdef DEBUG
1317 elf_end (elf);
1318
Ulrich Drepper0fe63532007-02-05 21:05:51 +00001319 arlib_fini ();
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001320
1321 close (fd);
1322#endif
1323
1324 return status;
1325 }
1326
1327 /* If we have no entry point so far add at the end. AFTER_MEMBERELEM
1328 being NULL when adding before an entry means add at the beginning. */
1329 if (ipos != ipos_before && after_memberelem == NULL)
1330 after_memberelem = all;
1331
1332 /* Convert the circular list into a normal list first. */
1333 if (all != NULL)
1334 {
1335 struct armem *tmp = all;
1336 all = all->next;
1337 tmp->next = NULL;
1338 }
1339
1340 struct armem *last_added = after_memberelem;
1341 for (int cnt = 0; cnt < argc; ++cnt)
1342 if (oper != oper_replace || found[cnt]->old_off == -1)
1343 {
1344 if (last_added == NULL)
1345 {
1346 found[cnt]->next = all;
1347 last_added = all = found[cnt];
1348 }
1349 else
1350 {
1351 found[cnt]->next = last_added->next;
1352 last_added = last_added->next = found[cnt];
1353 }
1354 }
1355
1356 /* Finally compute the offset and add the symbols for the files
1357 after the insert point. */
1358 if (likely (all != NULL))
1359 for (struct armem *memp = all; memp != NULL; memp = memp->next)
1360 {
1361 memp->off = cur_off;
1362
1363 if (memp->mem == NULL)
1364 {
1365 Elf_Arhdr *arhdr;
1366 /* Fake initializing arhdr and subelf to keep gcc calm. */
1367 asm ("" : "=m" (arhdr), "=m" (subelf));
1368 if (elf_rand (elf, memp->old_off) == 0
1369 || (subelf = elf_begin (fd, ELF_C_READ_MMAP, elf)) == NULL
1370 || (arhdr = elf_getarhdr (subelf)) == NULL)
1371 /* This should never happen since we already looked at the
1372 archive content. But who knows... */
1373 error (EXIT_FAILURE, 0, "%s: %s", arfname, elf_errmsg (-1));
1374
Ulrich Drepper0fe63532007-02-05 21:05:51 +00001375 arlib_add_symbols (subelf, arfname, arhdr->ar_name, cur_off);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001376
1377 elf_end (subelf);
1378 }
1379 else
Ulrich Drepper0fe63532007-02-05 21:05:51 +00001380 arlib_add_symbols (memp->elf, arfname, memp->name, cur_off);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001381
1382 cur_off += (((memp->size + 1) & ~((off_t) 1))
1383 + sizeof (struct ar_hdr));
1384 }
1385
1386 /* Now we have all the information for the symbol table and long
1387 file name table. Construct the final layout. */
Ulrich Drepper0fe63532007-02-05 21:05:51 +00001388 arlib_finalize ();
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001389
1390 /* Create a new, temporary file in the same directory as the
1391 original file. */
1392 char tmpfname[strlen (arfname) + 7];
1393 strcpy (stpcpy (tmpfname, arfname), "XXXXXX");
1394 int newfd;
1395 if (fd != -1)
1396 newfd = mkstemp (tmpfname);
1397 else
1398 {
1399 newfd = open (arfname, O_RDWR | O_CREAT | O_EXCL, DEFFILEMODE);
1400 if (newfd == -1 && errno == EEXIST)
1401 /* Bah, first the file did not exist, now it does. Restart. */
1402 return do_oper_insert (oper, arfname, argv, argc, member);
1403 }
1404 if (unlikely (newfd == -1))
1405 goto nonew;
1406
1407 /* Create the header. */
1408 if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
1409 {
1410 nonew_unlink:
1411 if (fd != -1)
1412 {
1413 // XXX Use /prof/self/fd/%d ???
1414 unlink (tmpfname);
1415 if (newfd != -1)
1416 close (newfd);
1417 }
1418 nonew:
1419 error (0, errno, gettext ("cannot create new file"));
1420 status = 1;
1421 goto errout;
1422 }
1423
1424 /* If the new archive is not empty we actually have something to do. */
1425 if (likely (all != NULL))
1426 {
1427 /* Write the symbol table or the long file name table or both. */
1428 if (symtab.symsnamelen != 0
1429 && ((write_retry (newfd, symtab.symsoff, symtab.symsofflen)
1430 != (ssize_t) symtab.symsofflen)
1431 || (write_retry (newfd, symtab.symsname, symtab.symsnamelen)
1432 != (ssize_t) symtab.symsnamelen)))
1433 goto nonew_unlink;
1434
1435 if (symtab.longnameslen > sizeof (struct ar_hdr)
1436 && (write_retry (newfd, symtab.longnames, symtab.longnameslen)
1437 != (ssize_t) symtab.longnameslen))
1438 goto nonew_unlink;
1439
1440 off_t start = -1;
1441 off_t len = -1;
1442
1443 while (all != NULL)
1444 {
1445 if (all->mem != NULL)
1446 {
1447 /* This is a new file. If there is anything from the
1448 archive left to be written do it now. */
1449 if (start != -1 && copy_content (elf, newfd, start, len))
1450 goto nonew_unlink;
1451
1452 start = -1;
1453 len = -1;
1454
1455 /* Create the header. */
1456 struct ar_hdr arhdr;
1457 char tmpbuf[sizeof (arhdr.ar_name) + 1];
1458 if (all->long_name_off == -1)
1459 {
1460 size_t namelen = strlen (all->name);
Ulrich Drepperb597dfa2007-10-16 05:21:27 +00001461 char *p = mempcpy (arhdr.ar_name, all->name, namelen);
1462 *p++ = '/';
1463 memset (p, ' ', sizeof (arhdr.ar_name) - namelen - 1);
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001464 }
1465 else
1466 {
1467 snprintf (tmpbuf, sizeof (arhdr.ar_name) + 1, "/%-*ld",
1468 (int) sizeof (arhdr.ar_name), all->long_name_off);
1469 memcpy (arhdr.ar_name, tmpbuf, sizeof (arhdr.ar_name));
1470 }
1471
1472 no0print (false, arhdr.ar_date, sizeof (arhdr.ar_date),
1473 all->sec);
1474 no0print (false, arhdr.ar_uid, sizeof (arhdr.ar_uid), all->uid);
1475 no0print (false, arhdr.ar_gid, sizeof (arhdr.ar_gid), all->gid);
1476 no0print (true, arhdr.ar_mode, sizeof (arhdr.ar_mode),
1477 all->mode);
1478 no0print (false, arhdr.ar_size, sizeof (arhdr.ar_size),
1479 all->size);
1480 memcpy (arhdr.ar_fmag, ARFMAG, sizeof (arhdr.ar_fmag));
1481
1482 if (unlikely (write_retry (newfd, &arhdr, sizeof (arhdr))
1483 != sizeof (arhdr)))
1484 goto nonew_unlink;
1485
1486 /* Now the file itself. */
1487 if (unlikely (write_retry (newfd, all->mem, all->size)
1488 != (off_t) all->size))
1489 goto nonew_unlink;
1490
1491 /* Pad the file if its size is odd. */
1492 if ((all->size & 1) != 0)
Ulrich Drepperb597dfa2007-10-16 05:21:27 +00001493 if (unlikely (write_retry (newfd, "\n", 1) != 1))
Ulrich Drepper93ab56f2007-02-05 22:12:41 +00001494 goto nonew_unlink;
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001495 }
1496 else
1497 {
1498 /* This is a member from the archive. */
1499 if (write_member (all, &start, &len, elf, cur_off, newfd)
1500 != 0)
1501 goto nonew_unlink;
1502 }
1503
1504 all = all->next;
1505 }
1506
1507 /* Write the last part. */
1508 if (start != -1 && copy_content (elf, newfd, start, len))
1509 goto nonew_unlink;
1510 }
1511
1512 /* Set the mode of the new file to the same values the original file
1513 has. */
1514 if (fd != -1
1515 && (fchmod (newfd, st.st_mode & ALLPERMS) != 0
1516 /* Never complain about fchown failing. */
Ulrich Drepper93ab56f2007-02-05 22:12:41 +00001517 || (({asm ("" :: "r" (fchown (newfd, st.st_uid, st.st_gid))); }),
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001518 close (newfd) != 0)
1519 || (newfd = -1, rename (tmpfname, arfname) != 0)))
1520 goto nonew_unlink;
1521
1522 errout:
1523#ifdef DEBUG
1524 elf_end (elf);
1525
Ulrich Drepper0fe63532007-02-05 21:05:51 +00001526 arlib_fini ();
Ulrich Drepperce0bdb62007-02-05 07:13:52 +00001527
1528 close (fd);
1529#endif
1530
1531 return status;
1532}
Ulrich Drepper3cbdd382008-01-02 17:44:39 +00001533
1534
1535#include "debugpred.h"