tar et al: die if bb_copyfd_size copies less than asked for.
[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         file_header_t *typed = archive_handle->file_header;
13         union {
14                 char raw[60];
15                 struct {
16                         char name[16];
17                         char date[12];
18                         char uid[6];
19                         char gid[6];
20                         char mode[8];
21                         char size[10];
22                         char magic[2];
23                 } formatted;
24         } ar;
25 #ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
26         static char *ar_long_names;
27         static unsigned int ar_long_name_size;
28 #endif
29
30         /* dont use xread as we want to handle the error ourself */
31         if (read(archive_handle->src_fd, ar.raw, 60) != 60) {
32                 /* End Of File */
33                 return EXIT_FAILURE;
34         }
35
36         /* ar header starts on an even byte (2 byte aligned)
37          * '\n' is used for padding
38          */
39         if (ar.raw[0] == '\n') {
40                 /* fix up the header, we started reading 1 byte too early */
41                 memmove(ar.raw, &ar.raw[1], 59);
42                 ar.raw[59] = xread_char(archive_handle->src_fd);
43                 archive_handle->offset++;
44         }
45         archive_handle->offset += 60;
46
47         /* align the headers based on the header magic */
48         if ((ar.formatted.magic[0] != '`') || (ar.formatted.magic[1] != '\n')) {
49                 bb_error_msg_and_die("invalid ar header");
50         }
51
52         typed->mode = xstrtoul(ar.formatted.mode, 8);
53         typed->mtime = xatou(ar.formatted.date);
54         typed->uid = xatou(ar.formatted.uid);
55         typed->gid = xatou(ar.formatted.gid);
56         typed->size = xatoul(ar.formatted.size);
57
58         /* long filenames have '/' as the first character */
59         if (ar.formatted.name[0] == '/') {
60 #ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
61                 if (ar.formatted.name[1] == '/') {
62                         /* If the second char is a '/' then this entries data section
63                          * stores long filename for multiple entries, they are stored
64                          * in static variable long_names for use in future entries */
65                         ar_long_name_size = typed->size;
66                         ar_long_names = xmalloc(ar_long_name_size);
67                         xread(archive_handle->src_fd, ar_long_names, ar_long_name_size);
68                         archive_handle->offset += ar_long_name_size;
69                         /* This ar entries data section only contained filenames for other records
70                          * they are stored in the static ar_long_names for future reference */
71                         return get_header_ar(archive_handle); /* Return next header */
72                 } else if (ar.formatted.name[1] == ' ') {
73                         /* This is the index of symbols in the file for compilers */
74                         data_skip(archive_handle);
75                         archive_handle->offset += typed->size;
76                         return get_header_ar(archive_handle); /* Return next header */
77                 } else {
78                         /* The number after the '/' indicates the offset in the ar data section
79                         (saved in variable long_name) that conatains the real filename */
80                         const unsigned int long_offset = atoi(&ar.formatted.name[1]);
81                         if (long_offset >= ar_long_name_size) {
82                                 bb_error_msg_and_die("can't resolve long filename");
83                         }
84                         typed->name = xstrdup(ar_long_names + long_offset);
85                 }
86 #else
87                 bb_error_msg_and_die("long filenames not supported");
88 #endif
89         } else {
90                 /* short filenames */
91                 typed->name = xstrndup(ar.formatted.name, 16);
92         }
93
94         typed->name[strcspn(typed->name, " /")] = '\0';
95
96         if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
97                 archive_handle->action_header(typed);
98                 if (archive_handle->sub_archive) {
99                         while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS)
100                                 /* repeat */;
101                 } else {
102                         archive_handle->action_data(archive_handle);
103                 }
104         } else {
105                 data_skip(archive_handle);
106         }
107
108         archive_handle->offset += typed->size;
109         /* Set the file pointer to the correct spot, we may have been reading a compressed file */
110         lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET);
111
112         return EXIT_SUCCESS;
113 }