25fc83781225c93a34accb5bf29a975ef7a418f4
[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 extern 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         /* Some ar entries have a trailing '\n' after the previous data entry */
51         if (ar.raw[0] == '\n') {
52                 /* fix up the header, we started reading 1 byte too early */
53                 memmove(ar.raw, &ar.raw[1], 59);
54                 ar.raw[59] = bb_xread_char(archive_handle->src_fd);
55                 archive_handle->offset++;
56         }
57         archive_handle->offset += 60;
58
59         /* align the headers based on the header magic */
60         if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) {
61                 bb_error_msg_and_die("Invalid ar header");
62         }
63
64         typed->mode = strtol(ar.formated.mode, NULL, 8);
65         typed->mtime = atoi(ar.formated.date);
66         typed->uid = atoi(ar.formated.uid);
67         typed->gid = atoi(ar.formated.gid);
68         typed->size = atoi(ar.formated.size);
69
70         /* long filenames have '/' as the first character */
71         if (ar.formated.name[0] == '/') {
72 #ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
73                 if (ar.formated.name[1] == '/') {
74                         /* If the second char is a '/' then this entries data section
75                          * stores long filename for multiple entries, they are stored
76                          * in static variable long_names for use in future entries */
77                         ar_long_name_size = typed->size;
78                         ar_long_names = xmalloc(ar_long_name_size);
79                         bb_xread_all(archive_handle->src_fd, ar_long_names, ar_long_name_size);
80                         archive_handle->offset += ar_long_name_size;
81                         /* This ar entries data section only contained filenames for other records
82                          * they are stored in the static ar_long_names for future reference */
83                         return (get_header_ar(archive_handle)); /* Return next header */
84                 } else if (ar.formated.name[1] == ' ') {
85                         /* This is the index of symbols in the file for compilers */
86                         data_skip(archive_handle);
87                         return (get_header_ar(archive_handle)); /* Return next header */
88                 } else {
89                         /* The number after the '/' indicates the offset in the ar data section
90                         (saved in variable long_name) that conatains the real filename */
91                         const unsigned int long_offset = atoi(&ar.formated.name[1]);
92                         if (long_offset >= ar_long_name_size) {
93                                 bb_error_msg_and_die("Cant resolve long filename");
94                         }
95                         typed->name = bb_xstrdup(ar_long_names + long_offset);
96                 }
97 #else
98                 bb_error_msg_and_die("long filenames not supported");
99 #endif
100         } else {
101                 /* short filenames */
102                typed->name = bb_xstrndup(ar.formated.name, 16);
103         }
104
105         typed->name[strcspn(typed->name, " /")] = '\0';
106
107         if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
108                 archive_handle->action_header(typed);
109                 if (archive_handle->sub_archive) {
110                         while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS);
111                 } else {
112                         archive_handle->action_data(archive_handle);
113                 }
114         } else {
115                 data_skip(archive_handle);                      
116         }
117
118         archive_handle->offset += typed->size;
119         /* Set the file pointer to the correct spot, we may have been reading a compressed file */
120         lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET);
121
122         return(EXIT_SUCCESS);
123 }
124