New dpkg-deb function -t, stands for --fsys-tarfile
[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, int untar_function, char *base_path)
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                         break;
61                 }
62
63                 uncompressed_count += 512;
64
65                 /* Check header has valid magic */
66                 if (strncmp(raw_tar_header.magic, "ustar", 5) != 0) {
67 /*
68  * FIXME, Need HELP with this
69  *
70  * This has to fail silently or it incorrectly reports errors when called from
71  * deb_extract.
72  * The problem is deb_extract decompresses the .gz file in a child process and
73  * untar reads from the child proccess. The child process finishes and exits,
74  * but fread reads 0's from the src_tar_file even though the child
75  * closed its handle.
76  */
77 //                      error_msg("Invalid tar magic");
78                         break;
79                 }
80
81                 /* Do checksum on headers */
82         for (i =  0; i < 148 ; i++) {
83                         sum += temp[i];
84                 }
85         sum += ' ' * 8;
86                 for (i =  156; i < 512 ; i++) {
87                         sum += temp[i];
88                 }
89         if (sum != strtol(raw_tar_header.chksum, NULL, 8)) {
90                         error_msg("Invalid tar header checksum");
91                         break;
92                 }
93
94                 /* convert to type'ed variables */
95         size = strtol(raw_tar_header.size, NULL, 8);
96                 parse_mode(raw_tar_header.mode, &mode);
97
98                 /* start of next header is at */
99                 next_header_offset = uncompressed_count + size;
100                 if (size % 512 != 0) {
101                         next_header_offset += (512 - size % 512);
102                 }
103
104                 /*
105                  * seek to start of control file, return length
106                  *
107                 if (dpkg_untar_function & dpkg_untar_seek_control) {
108                         if ((raw_tar_header.typeflag == '0') || (raw_tar_header.typeflag == '\0')) {
109                                 char *tar_filename;
110
111                                 tar_filename = strrchr(raw_tar_header.name, '/');
112                                 if (tar_filename == NULL) {
113                                         tar_filename = strdup(raw_tar_header.name);
114                                 } else {
115                                         tar_filename++;
116                                 }
117
118                                 if (strcmp(tar_filename, "control") == 0) {
119                                         return(size);
120                                 }
121                         }
122
123                 }
124 */
125                 if (untar_function & (extract_contents | extract_verbose_extract)) {
126                         printf("%s\n", raw_tar_header.name);
127                 }
128
129                 /* extract files */
130                 if (base_path != NULL) {
131                         dir = xmalloc(strlen(raw_tar_header.name) + strlen(base_path) + 2);
132                         sprintf(dir, "%s/%s", base_path, raw_tar_header.name);
133                         create_path(dir, 0777);
134                 }
135                 switch (raw_tar_header.typeflag ) {
136                         case '0':
137                         case '\0':
138                                 /* If the name ends in a '/' then assume it is
139                                  * supposed to be a directory, and fall through
140                                  */
141                                 if (raw_tar_header.name[strlen(raw_tar_header.name)-1] != '/') {
142                                         switch (untar_function) {
143                                                 case (extract_extract): {
144                                                                 FILE *dst_file = wfopen(dir, "w");
145                                                                 copy_file_chunk(src_tar_file, dst_file, size);
146                                                                 fclose(dst_file);
147                                                         }
148                                                         break;
149                                                 default: {
150                                                                 int remaining = size;
151                                                                 while (remaining-- > 0) {
152                                                                         fgetc(src_tar_file);
153                                                                 }
154                                                         }
155                                         }
156                                         uncompressed_count += size;
157                                         break;
158                                 }
159                         case '5':
160                                 if (untar_function & extract_extract) {
161                                         if (create_path(dir, mode) != TRUE) {
162                                                 free(dir);
163                                                 perror_msg("%s: Cannot mkdir", raw_tar_header.name); 
164                                                 return(EXIT_FAILURE);
165                                         }
166                                 }
167                                 break;
168                         case '1':
169                                 if (untar_function & extract_extract) {
170                                         if (link(raw_tar_header.linkname, raw_tar_header.name) < 0) {
171                                                 free(dir);
172                                                 perror_msg("%s: Cannot create hard link to '%s'", raw_tar_header.name, raw_tar_header.linkname); 
173                                                 return(EXIT_FAILURE);
174                                         }
175                                 }
176                                 break;
177                         case '2':
178                                 if (untar_function & extract_extract) {
179                                         if (symlink(raw_tar_header.linkname, raw_tar_header.name) < 0) {
180                                                 free(dir);
181                                                 perror_msg("%s: Cannot create symlink to '%s'", raw_tar_header.name, raw_tar_header.linkname); 
182                                                 return(EXIT_FAILURE);
183                                         }
184                                 }
185                                 break;
186                         case '3':
187                         case '4':
188                         case '6':
189 //                              if (tarExtractSpecial( &header, extractFlag, tostdoutFlag)==FALSE)
190 //                                      errorFlag=TRUE;
191 //                              break;
192                         default:
193                                 error_msg("Unknown file type '%c' in tar file", raw_tar_header.typeflag);
194                                 free(dir);
195                                 return(EXIT_FAILURE);
196                 }
197 //              free(dir);
198         }
199         return(EXIT_SUCCESS);
200 }