]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.bin/ar/write.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.git] / usr.bin / ar / write.c
1 /*-
2  * Copyright (c) 2007 Kai Wang
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/endian.h>
31 #include <sys/mman.h>
32 #include <sys/queue.h>
33 #include <sys/stat.h>
34 #include <archive.h>
35 #include <archive_entry.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <gelf.h>
39 #include <libgen.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sysexits.h>
44
45 #include "ar.h"
46
47 #define _ARMAG_LEN 8            /* length of ar magic string */
48 #define _ARHDR_LEN 60           /* length of ar header */
49 #define _INIT_AS_CAP 128        /* initial archive string table size */
50 #define _INIT_SYMOFF_CAP (256*(sizeof(uint32_t))) /* initial so table size */
51 #define _INIT_SYMNAME_CAP 1024                    /* initial sn table size */
52 #define _MAXNAMELEN_SVR4 15     /* max member name length in svr4 variant */
53 #define _TRUNCATE_LEN 15        /* number of bytes to keep for member name */
54
55 static void     add_to_ar_str_table(struct bsdar *bsdar, const char *name);
56 static void     add_to_ar_sym_table(struct bsdar *bsdar, const char *name);
57 static struct ar_obj    *create_obj_from_file(struct bsdar *bsdar,
58                     const char *name, time_t mtime);
59 static void     create_symtab_entry(struct bsdar *bsdar, void *maddr,
60                     size_t size);
61 static void     free_obj(struct bsdar *bsdar, struct ar_obj *obj);
62 static void     insert_obj(struct bsdar *bsdar, struct ar_obj *obj,
63                     struct ar_obj *pos);
64 static void     read_objs(struct bsdar *bsdar, const char *archive,
65                     int checkargv);
66 static void     write_archive(struct bsdar *bsdar, char mode);
67 static void     write_cleanup(struct bsdar *bsdar);
68 static void     write_data(struct bsdar *bsdar, struct archive *a,
69                     const void *buf, size_t s);
70 static void     write_objs(struct bsdar *bsdar);
71
72 void
73 ar_mode_d(struct bsdar *bsdar)
74 {
75
76         write_archive(bsdar, 'd');
77 }
78
79 void
80 ar_mode_m(struct bsdar *bsdar)
81 {
82
83         write_archive(bsdar, 'm');
84 }
85
86 void
87 ar_mode_q(struct bsdar *bsdar)
88 {
89
90         write_archive(bsdar, 'q');
91 }
92
93 void
94 ar_mode_r(struct bsdar *bsdar)
95 {
96
97         write_archive(bsdar, 'r');
98 }
99
100 void
101 ar_mode_s(struct bsdar *bsdar)
102 {
103
104         write_archive(bsdar, 's');
105 }
106
107 void
108 ar_mode_A(struct bsdar *bsdar)
109 {
110
111         write_archive(bsdar, 'A');
112 }
113
114 /*
115  * Create object from file, return created obj upon success, or NULL
116  * when an error occurs or the member is not newer than existing
117  * one while -u is specified.
118  */
119 static struct ar_obj *
120 create_obj_from_file(struct bsdar *bsdar, const char *name, time_t mtime)
121 {
122         struct ar_obj           *obj;
123         struct stat              sb;
124         const char              *bname;
125
126         if (name == NULL)
127                 return (NULL);
128
129         obj = malloc(sizeof(struct ar_obj));
130         if (obj == NULL)
131                 bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
132         if ((obj->fd = open(name, O_RDONLY, 0)) < 0) {
133                 bsdar_warnc(bsdar, errno, "can't open file: %s", name);
134                 free(obj);
135                 return (NULL);
136         }
137
138         if ((bname = basename(name)) == NULL)
139                 bsdar_errc(bsdar, EX_SOFTWARE, errno, "basename failed");
140         if (bsdar->options & AR_TR && strlen(bname) > _TRUNCATE_LEN) {
141                 if ((obj->name = malloc(_TRUNCATE_LEN + 1)) == NULL)
142                         bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
143                 (void)strncpy(obj->name, bname, _TRUNCATE_LEN);
144                 obj->name[_TRUNCATE_LEN] = '\0';
145         } else
146                 if ((obj->name = strdup(bname)) == NULL)
147                     bsdar_errc(bsdar, EX_SOFTWARE, errno, "strdup failed");
148
149         if (fstat(obj->fd, &sb) < 0) {
150                 bsdar_warnc(bsdar, errno, "can't fstat file: %s", obj->name);
151                 goto giveup;
152         }
153         if (!S_ISREG(sb.st_mode)) {
154                 bsdar_warnc(bsdar, 0, "%s is not an ordinary file", obj->name);
155                 goto giveup;
156         }
157
158         /*
159          * When option '-u' is specified and member is not newer than the
160          * existing one, the replace will not happen. While if mtime == 0,
161          * which indicates that this is to "replace a none exist member",
162          * the replace will proceed regardless of '-u'.
163          */
164         if (mtime != 0 && bsdar->options & AR_U && sb.st_mtime <= mtime)
165                 goto giveup;
166
167         /*
168          * When option '-D' is specified, mtime and UID / GID from the file
169          * will be replaced with 0, and file mode with 644. This ensures that 
170          * checksums will match for two archives containing the exact same
171          * files.
172          */
173         if (bsdar->options & AR_D) {
174                 obj->uid = 0;
175                 obj->gid = 0;
176                 obj->mtime = 0;
177                 obj->md = S_IFREG | 0644;
178         } else {
179                 obj->uid = sb.st_uid;
180                 obj->gid = sb.st_gid;
181                 obj->mtime = sb.st_mtime;
182                 obj->md = sb.st_mode;
183         }
184         obj->size = sb.st_size;
185         obj->dev = sb.st_dev;
186         obj->ino = sb.st_ino;
187
188         if (obj->size == 0) {
189                 obj->maddr = NULL;
190                 return (obj);
191         }
192
193         if ((obj->maddr = mmap(NULL, obj->size, PROT_READ,
194             MAP_PRIVATE, obj->fd, (off_t)0)) == MAP_FAILED) {
195                 bsdar_warnc(bsdar, errno, "can't mmap file: %s", obj->name);
196                 goto giveup;
197         }
198         if (close(obj->fd) < 0)
199                 bsdar_errc(bsdar, EX_SOFTWARE, errno, "close failed: %s",
200                     obj->name);
201
202         return (obj);
203
204 giveup:
205         if (close(obj->fd) < 0)
206                 bsdar_errc(bsdar, EX_SOFTWARE, errno, "close failed: %s",
207                     obj->name);
208         free(obj->name);
209         free(obj);
210         return (NULL);
211 }
212
213 /*
214  * Free object itself and its associated allocations.
215  */
216 static void
217 free_obj(struct bsdar *bsdar, struct ar_obj *obj)
218 {
219         if (obj->fd == -1)
220                 free(obj->maddr);
221         else
222                 if (obj->maddr != NULL && munmap(obj->maddr, obj->size))
223                         bsdar_warnc(bsdar, errno,
224                             "can't munmap file: %s", obj->name);
225         free(obj->name);
226         free(obj);
227 }
228
229 /*
230  * Insert obj to the tail, or before/after the pos obj.
231  */
232 static void
233 insert_obj(struct bsdar *bsdar, struct ar_obj *obj, struct ar_obj *pos)
234 {
235         if (obj == NULL)
236                 bsdar_errc(bsdar, EX_SOFTWARE, 0, "try to insert a null obj");
237
238         if (pos == NULL || obj == pos)
239                 /*
240                  * If the object to move happens to be the position obj,
241                  * or if there is not a pos obj, move it to tail.
242                  */
243                 goto tail;
244
245         if (bsdar->options & AR_B) {
246                 TAILQ_INSERT_BEFORE(pos, obj, objs);
247                 return;
248         }
249         if (bsdar->options & AR_A) {
250                 TAILQ_INSERT_AFTER(&bsdar->v_obj, pos, obj, objs);
251                 return;
252         }
253
254 tail:
255         TAILQ_INSERT_TAIL(&bsdar->v_obj, obj, objs);
256
257 }
258
259 /*
260  * Read objects from archive into v_obj list. Note that checkargv is
261  * set when read_objs is used to read objects from the target of
262  * ADDLIB command (ar script mode), in this case argv array possibly
263  * specifies the members ADDLIB want.
264  */
265 static void
266 read_objs(struct bsdar *bsdar, const char *archive, int checkargv)
267 {
268         struct archive           *a;
269         struct archive_entry     *entry;
270         struct ar_obj            *obj;
271         const char               *name;
272         const char               *bname;
273         char                     *buff;
274         char                    **av;
275         size_t                    size;
276         int                       i, r, find;
277
278         if ((a = archive_read_new()) == NULL)
279                 bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_read_new failed");
280         archive_read_support_format_ar(a);
281         AC(archive_read_open_filename(a, archive, DEF_BLKSZ));
282         for (;;) {
283                 r = archive_read_next_header(a, &entry);
284                 if (r == ARCHIVE_FATAL)
285                         bsdar_errc(bsdar, EX_DATAERR, 0, "%s",
286                             archive_error_string(a));
287                 if (r == ARCHIVE_EOF)
288                         break;
289                 if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY)
290                         bsdar_warnc(bsdar, 0, "%s", archive_error_string(a));
291                 if (r == ARCHIVE_RETRY) {
292                         bsdar_warnc(bsdar, 0, "Retrying...");
293                         continue;
294                 }
295
296                 name = archive_entry_pathname(entry);
297
298                 /*
299                  * skip pseudo members.
300                  */
301                 if (strcmp(name, "/") == 0 || strcmp(name, "//") == 0)
302                         continue;
303
304                 /*
305                  * If checkargv is set, only read those members specified
306                  * in argv.
307                  */
308                 if (checkargv && bsdar->argc > 0) {
309                         find = 0;
310                         for(i = 0; i < bsdar->argc; i++) {
311                                 av = &bsdar->argv[i];
312                                 if (*av == NULL)
313                                         continue;
314                                 if ((bname = basename(*av)) == NULL)
315                                         bsdar_errc(bsdar, EX_SOFTWARE, errno,
316                                             "basename failed");
317                                 if (strcmp(bname, name) != 0)
318                                         continue;
319
320                                 *av = NULL;
321                                 find = 1;
322                                 break;
323                         }
324                         if (!find)
325                                 continue;
326                 }
327
328                 size = archive_entry_size(entry);
329
330                 if (size > 0) {
331                         if ((buff = malloc(size)) == NULL)
332                                 bsdar_errc(bsdar, EX_SOFTWARE, errno,
333                                     "malloc failed");
334                         if (archive_read_data(a, buff, size) != (ssize_t)size) {
335                                 bsdar_warnc(bsdar, 0, "%s",
336                                     archive_error_string(a));
337                                 free(buff);
338                                 continue;
339                         }
340                 } else
341                         buff = NULL;
342
343                 obj = malloc(sizeof(struct ar_obj));
344                 if (obj == NULL)
345                         bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
346                 obj->maddr = buff;
347                 if ((obj->name = strdup(name)) == NULL)
348                         bsdar_errc(bsdar, EX_SOFTWARE, errno, "strdup failed");
349                 obj->size = size;
350                 obj->uid = archive_entry_uid(entry);
351                 obj->gid = archive_entry_gid(entry);
352                 obj->md = archive_entry_mode(entry);
353                 obj->mtime = archive_entry_mtime(entry);
354                 obj->dev = 0;
355                 obj->ino = 0;
356
357                 /*
358                  * Objects from archive have obj->fd set to -1,
359                  * for the ease of cleaning up.
360                  */
361                 obj->fd = -1;
362                 TAILQ_INSERT_TAIL(&bsdar->v_obj, obj, objs);
363         }
364         AC(archive_read_close(a));
365         AC(archive_read_free(a));
366 }
367
368 /*
369  * Determine the constitution of resulting archive.
370  */
371 static void
372 write_archive(struct bsdar *bsdar, char mode)
373 {
374         struct ar_obj            *nobj, *obj, *obj_temp, *pos;
375         struct stat               sb;
376         const char               *bname;
377         char                    **av;
378         int                       i;
379
380         TAILQ_INIT(&bsdar->v_obj);
381         nobj = NULL;
382         pos = NULL;
383         memset(&sb, 0, sizeof(sb));
384
385         /*
386          * Test if the specified archive exists, to figure out
387          * whether we are creating one here.
388          */
389         if (stat(bsdar->filename, &sb) != 0) {
390                 if (errno != ENOENT) {
391                         bsdar_warnc(bsdar, 0, "stat %s failed",
392                             bsdar->filename);
393                         return;
394                 }
395
396                 /* We do not create archive in mode 'd', 'm' and 's'.  */
397                 if (mode != 'r' && mode != 'q') {
398                         bsdar_warnc(bsdar, 0, "%s: no such file",
399                             bsdar->filename);
400                         return;
401                 }
402
403                 /* Issue a warning if -c is not specified when creating. */
404                 if (!(bsdar->options & AR_C))
405                         bsdar_warnc(bsdar, 0, "creating %s", bsdar->filename);
406                 goto new_archive;
407         }
408
409         /*
410          * First read members from existing archive.
411          */
412         read_objs(bsdar, bsdar->filename, 0);
413
414         /*
415          * For mode 's', no member will be moved, deleted or replaced.
416          */
417         if (mode == 's')
418                 goto write_objs;
419
420         /*
421          * For mode 'q', we don't need to adjust existing members either.
422          * Also, -a, -b and -i are ignored in this mode. New members are
423          * always inserted at tail.
424          */
425         if (mode == 'q')
426                 goto new_archive;
427
428         /*
429          * Mode 'A' adds the contents of another archive to the tail of
430          * current archive. Note that mode 'A' is a special mode for the
431          * ADDLIB command of the ar script mode. Currently there is no
432          * access to this function from the ar command line mode.
433          */
434         if (mode == 'A') {
435                 /*
436                  * Read objects from the target archive of ADDLIB command.
437                  * If there are members specified in argv, read those members
438                  * only, otherwise the entire archive will be read.
439                  */
440                 read_objs(bsdar, bsdar->addlib, 1);
441                 goto write_objs;
442         }
443
444         /*
445          * Try to find the position member specified by user.
446          */
447         if (bsdar->options & AR_A || bsdar->options & AR_B) {
448                 TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
449                         if (strcmp(obj->name, bsdar->posarg) == 0) {
450                                 pos = obj;
451                                 break;
452                         }
453                 }
454
455                 /*
456                  * If can't find `pos' specified by user,
457                  * silently insert objects at tail.
458                  */
459                 if (pos == NULL)
460                         bsdar->options &= ~(AR_A | AR_B);
461         }
462
463         for (i = 0; i < bsdar->argc; i++) {
464                 av = &bsdar->argv[i];
465
466                 TAILQ_FOREACH_SAFE(obj, &bsdar->v_obj, objs, obj_temp) {
467                         if ((bname = basename(*av)) == NULL)
468                                 bsdar_errc(bsdar, EX_SOFTWARE, errno,
469                                     "basename failed");
470                         if (bsdar->options & AR_TR) {
471                                 if (strncmp(bname, obj->name, _TRUNCATE_LEN))
472                                         continue;
473                         } else
474                                 if (strcmp(bname, obj->name) != 0)
475                                         continue;
476
477                         if (mode == 'r') {
478                                 /*
479                                  * if the new member is not qualified
480                                  * to replace the old one, skip it.
481                                  */
482                                 nobj = create_obj_from_file(bsdar, *av,
483                                     obj->mtime);
484                                 if (nobj == NULL)
485                                         goto skip_obj;
486                         }
487
488                         if (bsdar->options & AR_V)
489                                 (void)fprintf(stdout, "%c - %s\n", mode,
490                                     *av);
491
492                         TAILQ_REMOVE(&bsdar->v_obj, obj, objs);
493                         if (mode == 'd' || mode == 'r')
494                                 free_obj(bsdar, obj);
495
496                         if (mode == 'm')
497                                 insert_obj(bsdar, obj, pos);
498                         if (mode == 'r')
499                                 insert_obj(bsdar, nobj, pos);
500
501                 skip_obj:
502                         *av = NULL;
503                         break;
504                 }
505
506         }
507
508 new_archive:
509         /*
510          * When operating in mode 'r', directly add those user specified
511          * objects which do not exist in current archive. When operating
512          * in mode 'q', all objects specified in command line args are
513          * appended to the archive, without comparing with existing ones.
514          */
515         for (i = 0; i < bsdar->argc; i++) {
516                 av = &bsdar->argv[i];
517                 if (*av != NULL && (mode == 'r' || mode == 'q')) {
518                         nobj = create_obj_from_file(bsdar, *av, 0);
519                         if (nobj != NULL)
520                                 insert_obj(bsdar, nobj, pos);
521                         if (bsdar->options & AR_V && nobj != NULL)
522                                 (void)fprintf(stdout, "a - %s\n", *av);
523                         *av = NULL;
524                 }
525         }
526
527 write_objs:
528         write_objs(bsdar);
529         write_cleanup(bsdar);
530 }
531
532 /*
533  * Memory cleaning up.
534  */
535 static void
536 write_cleanup(struct bsdar *bsdar)
537 {
538         struct ar_obj           *obj, *obj_temp;
539
540         TAILQ_FOREACH_SAFE(obj, &bsdar->v_obj, objs, obj_temp) {
541                 TAILQ_REMOVE(&bsdar->v_obj, obj, objs);
542                 free_obj(bsdar, obj);
543         }
544
545         free(bsdar->as);
546         free(bsdar->s_so);
547         free(bsdar->s_sn);
548         bsdar->as = NULL;
549         bsdar->s_so = NULL;
550         bsdar->s_sn = NULL;
551 }
552
553 /*
554  * Wrapper for archive_write_data().
555  */
556 static void
557 write_data(struct bsdar *bsdar, struct archive *a, const void *buf, size_t s)
558 {
559         if (archive_write_data(a, buf, s) != (ssize_t)s)
560                 bsdar_errc(bsdar, EX_SOFTWARE, 0, "%s",
561                     archive_error_string(a));
562 }
563
564 /*
565  * Write the resulting archive members.
566  */
567 static void
568 write_objs(struct bsdar *bsdar)
569 {
570         struct ar_obj           *obj;
571         struct archive          *a;
572         struct archive_entry    *entry;
573         size_t s_sz;            /* size of archive symbol table. */
574         size_t pm_sz;           /* size of pseudo members */
575         int                      i, nr;
576
577         if (elf_version(EV_CURRENT) == EV_NONE)
578                 bsdar_errc(bsdar, EX_SOFTWARE, 0,
579                     "ELF library initialization failed: %s", elf_errmsg(-1));
580
581         bsdar->rela_off = 0;
582
583         /* Create archive symbol table and archive string table, if need. */
584         TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
585                 if (!(bsdar->options & AR_SS) && obj->maddr != NULL)
586                         create_symtab_entry(bsdar, obj->maddr, obj->size);
587                 if (strlen(obj->name) > _MAXNAMELEN_SVR4)
588                         add_to_ar_str_table(bsdar, obj->name);
589                 bsdar->rela_off += _ARHDR_LEN + obj->size + obj->size % 2;
590         }
591
592         /*
593          * Pad the symbol name string table. It is treated specially because
594          * symbol name table should be padded by a '\0', not the common '\n'
595          * for other members. The size of sn table includes the pad bit.
596          */
597         if (bsdar->s_cnt != 0 && bsdar->s_sn_sz % 2 != 0)
598                 bsdar->s_sn[bsdar->s_sn_sz++] = '\0';
599
600         /*
601          * Archive string table is padded by a "\n" as the normal members.
602          * The difference is that the size of archive string table counts
603          * in the pad bit, while normal members' size fileds do not.
604          */
605         if (bsdar->as != NULL && bsdar->as_sz % 2 != 0)
606                 bsdar->as[bsdar->as_sz++] = '\n';
607
608         /*
609          * If there is a symbol table, calculate the size of pseudo members,
610          * convert previously stored relative offsets to absolute ones, and
611          * then make them Big Endian.
612          *
613          * absolute_offset = htobe32(relative_offset + size_of_pseudo_members)
614          */
615
616         if (bsdar->s_cnt != 0) {
617                 s_sz = (bsdar->s_cnt + 1) * sizeof(uint32_t) + bsdar->s_sn_sz;
618                 pm_sz = _ARMAG_LEN + (_ARHDR_LEN + s_sz);
619                 if (bsdar->as != NULL)
620                         pm_sz += _ARHDR_LEN + bsdar->as_sz;
621                 for (i = 0; (size_t)i < bsdar->s_cnt; i++)
622                         *(bsdar->s_so + i) = htobe32(*(bsdar->s_so + i) +
623                             pm_sz);
624         }
625
626         if ((a = archive_write_new()) == NULL)
627                 bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_write_new failed");
628
629         archive_write_set_format_ar_svr4(a);
630
631         AC(archive_write_open_filename(a, bsdar->filename));
632
633         /*
634          * write the archive symbol table, if there is one.
635          * If options -s is explicitly specified or we are invoked
636          * as ranlib, write the symbol table even if it is empty.
637          */
638         if ((bsdar->s_cnt != 0 && !(bsdar->options & AR_SS)) ||
639             bsdar->options & AR_S) {
640                 entry = archive_entry_new();
641                 archive_entry_copy_pathname(entry, "/");
642                 if ((bsdar->options & AR_D) == 0)
643                         archive_entry_set_mtime(entry, time(NULL), 0);
644                 archive_entry_set_size(entry, (bsdar->s_cnt + 1) *
645                     sizeof(uint32_t) + bsdar->s_sn_sz);
646                 AC(archive_write_header(a, entry));
647                 nr = htobe32(bsdar->s_cnt);
648                 write_data(bsdar, a, &nr, sizeof(uint32_t));
649                 write_data(bsdar, a, bsdar->s_so, sizeof(uint32_t) *
650                     bsdar->s_cnt);
651                 write_data(bsdar, a, bsdar->s_sn, bsdar->s_sn_sz);
652                 archive_entry_free(entry);
653         }
654
655         /* write the archive string table, if any. */
656         if (bsdar->as != NULL) {
657                 entry = archive_entry_new();
658                 archive_entry_copy_pathname(entry, "//");
659                 archive_entry_set_size(entry, bsdar->as_sz);
660                 AC(archive_write_header(a, entry));
661                 write_data(bsdar, a, bsdar->as, bsdar->as_sz);
662                 archive_entry_free(entry);
663         }
664
665         /* write normal members. */
666         TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
667                 entry = archive_entry_new();
668                 archive_entry_copy_pathname(entry, obj->name);
669                 archive_entry_set_uid(entry, obj->uid);
670                 archive_entry_set_gid(entry, obj->gid);
671                 archive_entry_set_mode(entry, obj->md);
672                 archive_entry_set_size(entry, obj->size);
673                 archive_entry_set_mtime(entry, obj->mtime, 0);
674                 archive_entry_set_dev(entry, obj->dev);
675                 archive_entry_set_ino(entry, obj->ino);
676                 archive_entry_set_filetype(entry, AE_IFREG);
677                 AC(archive_write_header(a, entry));
678                 write_data(bsdar, a, obj->maddr, obj->size);
679                 archive_entry_free(entry);
680         }
681
682         AC(archive_write_close(a));
683         AC(archive_write_free(a));
684 }
685
686 /*
687  * Extract global symbols from ELF binary members.
688  */
689 static void
690 create_symtab_entry(struct bsdar *bsdar, void *maddr, size_t size)
691 {
692         Elf             *e;
693         Elf_Scn         *scn;
694         GElf_Shdr        shdr;
695         GElf_Sym         sym;
696         Elf_Data        *data;
697         char            *name;
698         size_t           n, shstrndx;
699         int              elferr, tabndx, len, i;
700
701         if ((e = elf_memory(maddr, size)) == NULL) {
702                 bsdar_warnc(bsdar, 0, "elf_memory() failed: %s",
703                      elf_errmsg(-1));
704                 return;
705         }
706         if (elf_kind(e) != ELF_K_ELF) {
707                 /* Silently ignore non-elf member. */
708                 elf_end(e);
709                 return;
710         }
711         if (elf_getshstrndx(e, &shstrndx) == 0) {
712                 bsdar_warnc(bsdar, EX_SOFTWARE, 0, "elf_getshstrndx failed: %s",
713                      elf_errmsg(-1));
714                 elf_end(e);
715                 return;
716         }
717
718         tabndx = -1;
719         scn = NULL;
720         while ((scn = elf_nextscn(e, scn)) != NULL) {
721                 if (gelf_getshdr(scn, &shdr) != &shdr) {
722                         bsdar_warnc(bsdar, 0,
723                             "elf_getshdr failed: %s", elf_errmsg(-1));
724                         continue;
725                 }
726                 if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL) {
727                         bsdar_warnc(bsdar, 0,
728                             "elf_strptr failed: %s", elf_errmsg(-1));
729                         continue;
730                 }
731                 if (strcmp(name, ".strtab") == 0) {
732                         tabndx = elf_ndxscn(scn);
733                         break;
734                 }
735         }
736         elferr = elf_errno();
737         if (elferr != 0)
738                 bsdar_warnc(bsdar, 0, "elf_nextscn failed: %s",
739                      elf_errmsg(elferr));
740         if (tabndx == -1) {
741                 bsdar_warnc(bsdar, 0, "can't find .strtab section");
742                 elf_end(e);
743                 return;
744         }
745
746         scn = NULL;
747         while ((scn = elf_nextscn(e, scn)) != NULL) {
748                 if (gelf_getshdr(scn, &shdr) != &shdr) {
749                         bsdar_warnc(bsdar, EX_SOFTWARE, 0,
750                             "elf_getshdr failed: %s", elf_errmsg(-1));
751                         continue;
752                 }
753                 if (shdr.sh_type != SHT_SYMTAB)
754                         continue;
755
756                 data = NULL;
757                 n = 0;
758                 while (n < shdr.sh_size &&
759                     (data = elf_getdata(scn, data)) != NULL) {
760                         len = data->d_size / shdr.sh_entsize;
761                         for (i = 0; i < len; i++) {
762                                 if (gelf_getsym(data, i, &sym) != &sym) {
763                                         bsdar_warnc(bsdar, EX_SOFTWARE, 0,
764                                             "gelf_getsym failed: %s",
765                                              elf_errmsg(-1));
766                                         continue;
767                                 }
768
769                                 /* keep only global or weak symbols */
770                                 if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL &&
771                                     GELF_ST_BIND(sym.st_info) != STB_WEAK)
772                                         continue;
773
774                                 /* keep only defined symbols */
775                                 if (sym.st_shndx == SHN_UNDEF)
776                                         continue;
777
778                                 if ((name = elf_strptr(e, tabndx,
779                                     sym.st_name)) == NULL) {
780                                         bsdar_warnc(bsdar, EX_SOFTWARE, 0,
781                                             "elf_strptr failed: %s",
782                                              elf_errmsg(-1));
783                                         continue;
784                                 }
785
786                                 add_to_ar_sym_table(bsdar, name);
787                         }
788                 }
789         }
790         elferr = elf_errno();
791         if (elferr != 0)
792                 bsdar_warnc(bsdar, EX_SOFTWARE, 0, "elf_nextscn failed: %s",
793                      elf_errmsg(elferr));
794
795         elf_end(e);
796 }
797
798 /*
799  * Append to the archive string table buffer.
800  */
801 static void
802 add_to_ar_str_table(struct bsdar *bsdar, const char *name)
803 {
804
805         if (bsdar->as == NULL) {
806                 bsdar->as_cap = _INIT_AS_CAP;
807                 bsdar->as_sz = 0;
808                 if ((bsdar->as = malloc(bsdar->as_cap)) == NULL)
809                         bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
810         }
811
812         /*
813          * The space required for holding one member name in as table includes:
814          * strlen(name) + (1 for '/') + (1 for '\n') + (possibly 1 for padding).
815          */
816         while (bsdar->as_sz + strlen(name) + 3 > bsdar->as_cap) {
817                 bsdar->as_cap *= 2;
818                 bsdar->as = realloc(bsdar->as, bsdar->as_cap);
819                 if (bsdar->as == NULL)
820                         bsdar_errc(bsdar, EX_SOFTWARE, errno, "realloc failed");
821         }
822         strncpy(&bsdar->as[bsdar->as_sz], name, strlen(name));
823         bsdar->as_sz += strlen(name);
824         bsdar->as[bsdar->as_sz++] = '/';
825         bsdar->as[bsdar->as_sz++] = '\n';
826 }
827
828 /*
829  * Append to the archive symbol table buffer.
830  */
831 static void
832 add_to_ar_sym_table(struct bsdar *bsdar, const char *name)
833 {
834
835         if (bsdar->s_so == NULL) {
836                 if ((bsdar->s_so = malloc(_INIT_SYMOFF_CAP)) ==
837                     NULL)
838                         bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
839                 bsdar->s_so_cap = _INIT_SYMOFF_CAP;
840                 bsdar->s_cnt = 0;
841         }
842
843         if (bsdar->s_sn == NULL) {
844                 if ((bsdar->s_sn = malloc(_INIT_SYMNAME_CAP)) == NULL)
845                         bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
846                 bsdar->s_sn_cap = _INIT_SYMNAME_CAP;
847                 bsdar->s_sn_sz = 0;
848         }
849
850         if (bsdar->s_cnt * sizeof(uint32_t) >= bsdar->s_so_cap) {
851                 bsdar->s_so_cap *= 2;
852                 bsdar->s_so = realloc(bsdar->s_so, bsdar->s_so_cap);
853                 if (bsdar->s_so == NULL)
854                         bsdar_errc(bsdar, EX_SOFTWARE, errno, "realloc failed");
855         }
856         bsdar->s_so[bsdar->s_cnt] = bsdar->rela_off;
857         bsdar->s_cnt++;
858
859         /*
860          * The space required for holding one symbol name in sn table includes:
861          * strlen(name) + (1 for '\n') + (possibly 1 for padding).
862          */
863         while (bsdar->s_sn_sz + strlen(name) + 2 > bsdar->s_sn_cap) {
864                 bsdar->s_sn_cap *= 2;
865                 bsdar->s_sn = realloc(bsdar->s_sn, bsdar->s_sn_cap);
866                 if (bsdar->s_sn == NULL)
867                         bsdar_errc(bsdar, EX_SOFTWARE, errno, "realloc failed");
868         }
869         strncpy(&bsdar->s_sn[bsdar->s_sn_sz], name, strlen(name));
870         bsdar->s_sn_sz += strlen(name);
871         bsdar->s_sn[bsdar->s_sn_sz++] = '\0';
872 }