- typos: s/compatability/compatibility/g;s/compatable/compatible/g;
[oweals/busybox.git] / archival / libunarchive / get_header_ar.c
1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU Library General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include "unarchive.h"
22 #include "libbb.h"
23
24 char get_header_ar(archive_handle_t *archive_handle)
25 {
26         file_header_t *typed = archive_handle->file_header;
27         union {
28                 char raw[60];
29                 struct {
30                         char name[16];
31                         char date[12];
32                         char uid[6];
33                         char gid[6];
34                         char mode[8];
35                         char size[10];
36                         char magic[2];
37                 } formated;
38         } ar;
39 #ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
40         static char *ar_long_names;
41         static unsigned int ar_long_name_size;
42 #endif
43
44         /* dont use bb_xread as we want to handle the error ourself */
45         if (read(archive_handle->src_fd, ar.raw, 60) != 60) {
46                 /* End Of File */
47                 return(EXIT_FAILURE);
48         }
49
50         /* ar header starts on an even byte (2 byte aligned)
51          * '\n' is used for padding
52          */
53         if (ar.raw[0] == '\n') {
54                 /* fix up the header, we started reading 1 byte too early */
55                 memmove(ar.raw, &ar.raw[1], 59);
56                 ar.raw[59] = bb_xread_char(archive_handle->src_fd);
57                 archive_handle->offset++;
58         }
59         archive_handle->offset += 60;
60
61         /* align the headers based on the header magic */
62         if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) {
63                 bb_error_msg_and_die("Invalid ar header");
64         }
65
66         typed->mode = strtol(ar.formated.mode, NULL, 8);
67         typed->mtime = atoi(ar.formated.date);
68         typed->uid = atoi(ar.formated.uid);
69         typed->gid = atoi(ar.formated.gid);
70         typed->size = atoi(ar.formated.size);
71
72         /* long filenames have '/' as the first character */
73         if (ar.formated.name[0] == '/') {
74 #ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
75                 if (ar.formated.name[1] == '/') {
76                         /* If the second char is a '/' then this entries data section
77                          * stores long filename for multiple entries, they are stored
78                          * in static variable long_names for use in future entries */
79                         ar_long_name_size = typed->size;
80                         ar_long_names = xmalloc(ar_long_name_size);
81                         bb_xread_all(archive_handle->src_fd, ar_long_names, ar_long_name_size);
82                         archive_handle->offset += ar_long_name_size;
83                         /* This ar entries data section only contained filenames for other records
84                          * they are stored in the static ar_long_names for future reference */
85                         return (get_header_ar(archive_handle)); /* Return next header */
86                 } else if (ar.formated.name[1] == ' ') {
87                         /* This is the index of symbols in the file for compilers */
88                         data_skip(archive_handle);
89                         archive_handle->offset += typed->size;
90                         return (get_header_ar(archive_handle)); /* Return next header */
91                 } else {
92                         /* The number after the '/' indicates the offset in the ar data section
93                         (saved in variable long_name) that conatains the real filename */
94                         const unsigned int long_offset = atoi(&ar.formated.name[1]);
95                         if (long_offset >= ar_long_name_size) {
96                                 bb_error_msg_and_die("Cant resolve long filename");
97                         }
98                         typed->name = bb_xstrdup(ar_long_names + long_offset);
99                 }
100 #else
101                 bb_error_msg_and_die("long filenames not supported");
102 #endif
103         } else {
104                 /* short filenames */
105                typed->name = bb_xstrndup(ar.formated.name, 16);
106         }
107
108         typed->name[strcspn(typed->name, " /")] = '\0';
109
110         if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
111                 archive_handle->action_header(typed);
112                 if (archive_handle->sub_archive) {
113                         while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS);
114                 } else {
115                         archive_handle->action_data(archive_handle);
116                 }
117         } else {
118                 data_skip(archive_handle);
119         }
120
121         archive_handle->offset += typed->size;
122         /* Set the file pointer to the correct spot, we may have been reading a compressed file */
123         lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET);
124
125         return(EXIT_SUCCESS);
126 }