merge post-1.3.0 fixes
[oweals/busybox.git] / archival / libunarchive / get_header_ar.c
1 /* vi: set sw=4 ts=4: */
2 /* Copyright 2001 Glenn McGrath.
3  *
4  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
5  */
6
7 #include "libbb.h"
8 #include "unarchive.h"
9
10 char get_header_ar(archive_handle_t *archive_handle)
11 {
12         int err;
13         file_header_t *typed = archive_handle->file_header;
14         union {
15                 char raw[60];
16                 struct {
17                         char name[16];
18                         char date[12];
19                         char uid[6];
20                         char gid[6];
21                         char mode[8];
22                         char size[10];
23                         char magic[2];
24                 } formatted;
25         } ar;
26 #ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
27         static char *ar_long_names;
28         static unsigned int ar_long_name_size;
29 #endif
30
31         /* dont use xread as we want to handle the error ourself */
32         if (read(archive_handle->src_fd, ar.raw, 60) != 60) {
33                 /* End Of File */
34                 return EXIT_FAILURE;
35         }
36
37         /* ar header starts on an even byte (2 byte aligned)
38          * '\n' is used for padding
39          */
40         if (ar.raw[0] == '\n') {
41                 /* fix up the header, we started reading 1 byte too early */
42                 memmove(ar.raw, &ar.raw[1], 59);
43                 ar.raw[59] = xread_char(archive_handle->src_fd);
44                 archive_handle->offset++;
45         }
46         archive_handle->offset += 60;
47
48         /* align the headers based on the header magic */
49         if (ar.formatted.magic[0] != '`' || ar.formatted.magic[1] != '\n')
50                 bb_error_msg_and_die("invalid ar header");
51
52         /* FIXME: more thorough routine would be in order here */
53         /* (we have something like that in tar) */
54         /* but for now we are lax. This code works because */
55         /* on misformatted numbers bb_strtou returns all-ones */
56         typed->mode = err = bb_strtou(ar.formatted.mode, NULL, 8);
57         if (err == -1) bb_error_msg_and_die("invalid ar header");
58         typed->mtime = err = bb_strtou(ar.formatted.date, NULL, 10);
59         if (err == -1) bb_error_msg_and_die("invalid ar header");
60         typed->uid = err = bb_strtou(ar.formatted.uid, NULL, 10);
61         if (err == -1) bb_error_msg_and_die("invalid ar header");
62         typed->gid = err = bb_strtou(ar.formatted.gid, NULL, 10);
63         if (err == -1) bb_error_msg_and_die("invalid ar header");
64         typed->size = err = bb_strtou(ar.formatted.size, NULL, 10);
65         if (err == -1) bb_error_msg_and_die("invalid ar header");
66
67         /* long filenames have '/' as the first character */
68         if (ar.formatted.name[0] == '/') {
69 #ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
70                 if (ar.formatted.name[1] == '/') {
71                         /* If the second char is a '/' then this entries data section
72                          * stores long filename for multiple entries, they are stored
73                          * in static variable long_names for use in future entries */
74                         ar_long_name_size = typed->size;
75                         ar_long_names = xmalloc(ar_long_name_size);
76                         xread(archive_handle->src_fd, ar_long_names, ar_long_name_size);
77                         archive_handle->offset += ar_long_name_size;
78                         /* This ar entries data section only contained filenames for other records
79                          * they are stored in the static ar_long_names for future reference */
80                         return get_header_ar(archive_handle); /* Return next header */
81                 } else if (ar.formatted.name[1] == ' ') {
82                         /* This is the index of symbols in the file for compilers */
83                         data_skip(archive_handle);
84                         archive_handle->offset += typed->size;
85                         return get_header_ar(archive_handle); /* Return next header */
86                 } else {
87                         /* The number after the '/' indicates the offset in the ar data section
88                         (saved in variable long_name) that conatains the real filename */
89                         const unsigned int long_offset = atoi(&ar.formatted.name[1]);
90                         if (long_offset >= ar_long_name_size) {
91                                 bb_error_msg_and_die("can't resolve long filename");
92                         }
93                         typed->name = xstrdup(ar_long_names + long_offset);
94                 }
95 #else
96                 bb_error_msg_and_die("long filenames not supported");
97 #endif
98         } else {
99                 /* short filenames */
100                 typed->name = xstrndup(ar.formatted.name, 16);
101         }
102
103         typed->name[strcspn(typed->name, " /")] = '\0';
104
105         if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
106                 archive_handle->action_header(typed);
107                 if (archive_handle->sub_archive) {
108                         while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS)
109                                 /* repeat */;
110                 } else {
111                         archive_handle->action_data(archive_handle);
112                 }
113         } else {
114                 data_skip(archive_handle);
115         }
116
117         archive_handle->offset += typed->size;
118         /* Set the file pointer to the correct spot, we may have been reading a compressed file */
119         lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET);
120
121         return EXIT_SUCCESS;
122 }