Read a FILE* till an empty line or eof and return it as a char buffer.
[oweals/busybox.git] / libbb / untar.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  * NOTE: This is used by deb_extract to avoid calling the whole tar applet.
17  * Its functionality should be merged with tar to avoid duplication
18  */
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include "libbb.h"
24
25 extern int untar(FILE *src_tar_file, const int untar_function, const char *argument)
26 {
27         typedef struct raw_tar_header {
28         char name[100];               /*   0-99 */
29         char mode[8];                 /* 100-107 */
30         char uid[8];                  /* 108-115 */
31         char gid[8];                  /* 116-123 */
32         char size[12];                /* 124-135 */
33         char mtime[12];               /* 136-147 */
34         char chksum[8];               /* 148-155 */
35         char typeflag;                /* 156-156 */
36         char linkname[100];           /* 157-256 */
37         char magic[6];                /* 257-262 */
38         char version[2];              /* 263-264 */
39         char uname[32];               /* 265-296 */
40         char gname[32];               /* 297-328 */
41         char devmajor[8];             /* 329-336 */
42         char devminor[8];             /* 337-344 */
43         char prefix[155];             /* 345-499 */
44         char padding[12];             /* 500-512 */
45         } raw_tar_header_t;
46
47         raw_tar_header_t raw_tar_header;
48         unsigned char *temp = (unsigned char *) &raw_tar_header;
49         long i;
50         long next_header_offset = 0;
51         long uncompressed_count = 0;
52         size_t size;
53         mode_t mode;
54
55         while (fread((char *) &raw_tar_header, 1, 512, src_tar_file) == 512) {
56                 long sum = 0;
57                 char *dir = NULL;
58                 
59                 if (ferror(src_tar_file) || feof(src_tar_file)) {
60                         perror_msg("untar: ");
61                         break;
62                 }
63
64                 uncompressed_count += 512;
65
66                 /* Check header has valid magic */
67                 if (strncmp(raw_tar_header.magic, "ustar", 5) != 0) {
68 /*
69  * FIXME, Need HELP with this
70  *
71  * This has to fail silently or it incorrectly reports errors when called from
72  * deb_extract.
73  * The problem is deb_extract decompresses the .gz file in a child process and
74  * untar reads from the child proccess. The child process finishes and exits,
75  * but fread reads 0's from the src_tar_file even though the child
76  * closed its handle.
77  */
78 //                      error_msg("Invalid tar magic");
79                         break;
80                 }
81
82                 /* Do checksum on headers */
83         for (i =  0; i < 148 ; i++) {
84                         sum += temp[i];
85                 }
86         sum += ' ' * 8;
87                 for (i =  156; i < 512 ; i++) {
88                         sum += temp[i];
89                 }
90         if (sum != strtol(raw_tar_header.chksum, NULL, 8)) {
91                         error_msg("Invalid tar header checksum");
92                         break;
93                 }
94
95                 /* convert to type'ed variables */
96         size = strtol(raw_tar_header.size, NULL, 8);
97                 parse_mode(raw_tar_header.mode, &mode);
98
99                 /* start of next header is at */
100                 next_header_offset = uncompressed_count + size;
101                 if (size % 512 != 0) {
102                         next_header_offset += (512 - size % 512);
103                 }
104
105                 if (untar_function & (extract_contents | extract_verbose_extract)) {
106                         printf("%s\n", raw_tar_header.name);
107                 }
108
109                 /* extract files */
110                 if (untar_function & (extract_extract | extract_verbose_extract | extract_control)) {
111                         dir = xmalloc(strlen(raw_tar_header.name) + strlen(argument) + 2);
112                         sprintf(dir, "%s/%s", argument, raw_tar_header.name);
113                         create_path(dir, 0777);
114                 }
115
116                 switch (raw_tar_header.typeflag ) {
117                         case '0':
118                         case '\0':
119                                 /* If the name ends in a '/' then assume it is
120                                  * supposed to be a directory, and fall through
121                                  */
122                                 if (raw_tar_header.name[strlen(raw_tar_header.name)-1] != '/') {
123                                         if (untar_function & (extract_extract | extract_verbose_extract | extract_control)) {
124                                                 FILE *dst_file = wfopen(dir, "w");
125                                                 copy_file_chunk(src_tar_file, dst_file, (unsigned long long) size);
126                                                 uncompressed_count += size;
127                                                 fclose(dst_file);
128                                         }
129                                         else if (untar_function & extract_info) {
130                                                 if (strstr(raw_tar_header.name, argument) != NULL) {
131                                                         copy_file_chunk(src_tar_file, stdout, (unsigned long long) size);
132                                                         uncompressed_count += size;
133                                                 }
134                                         }
135                                         else if (untar_function & extract_field) {
136                                                 if (strstr(raw_tar_header.name, "./control") != NULL) {
137                                                         char *line;
138                                                         while ((line = get_line_from_file(src_tar_file)) != NULL) {
139                                                                 uncompressed_count += strlen(line);
140                                                                 if (argument == NULL) {
141                                                                         printf("%s",line);
142                                                                 }
143                                                                 else if (strncmp(line, argument, strlen(argument)) == 0) {
144                                                                         char *file_ptr;
145                                                                         file_ptr = strstr(line, ": ");
146                                                                         printf("%s", file_ptr + 2);
147                                                                 }
148                                                         }
149                                                 }
150                                         }
151                                         break;
152                                 }
153                         case '5':
154                                 if (untar_function & (extract_extract | extract_verbose_extract | extract_control)) {
155                                         if (create_path(dir, mode) != TRUE) {
156                                                 free(dir);
157                                                 perror_msg("%s: Cannot mkdir", raw_tar_header.name); 
158                                                 return(EXIT_FAILURE);
159                                         }
160                                 }
161                                 break;
162                         case '1':
163                                 if (untar_function & (extract_extract | extract_verbose_extract | extract_control)) {
164                                         if (link(raw_tar_header.linkname, raw_tar_header.name) < 0) {
165                                                 free(dir);
166                                                 perror_msg("%s: Cannot create hard link to '%s'", raw_tar_header.name, raw_tar_header.linkname); 
167                                                 return(EXIT_FAILURE);
168                                         }
169                                 }
170                                 break;
171                         case '2':
172                                 if (untar_function & (extract_extract | extract_verbose_extract | extract_control)) {
173                                         if (symlink(raw_tar_header.linkname, raw_tar_header.name) < 0) {
174                                                 free(dir);
175                                                 perror_msg("%s: Cannot create symlink to '%s'", raw_tar_header.name, raw_tar_header.linkname); 
176                                                 return(EXIT_FAILURE);
177                                         }
178                                 }
179                                 break;
180                         case '3':
181                         case '4':
182                         case '6':
183 //                              if (tarExtractSpecial( &header, extractFlag, tostdoutFlag)==FALSE)
184 //                                      errorFlag=TRUE;
185 //                              break;
186                         default:
187                                 error_msg("Unknown file type '%c' in tar file", raw_tar_header.typeflag);
188                                 free(dir);
189                                 return(EXIT_FAILURE);
190                 }
191
192                 /*
193                  * Seek to start of next block, cant use fseek as unzip() does support it
194                  */
195                 while (uncompressed_count < next_header_offset) {
196                         if (fgetc(src_tar_file) == EOF) {
197                                 break;
198                         }
199                         uncompressed_count++;
200                 }
201
202 //              free(dir);
203         }
204         return(EXIT_SUCCESS);
205 }