Initial public busybox upstream commit
[busybox4maemo] / archival / libunarchive / get_header_tar.c
1 /* vi: set sw=4 ts=4: */
2 /* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
3  *
4  *  FIXME:
5  *    In privileged mode if uname and gname map to a uid and gid then use the
6  *    mapped value instead of the uid/gid values in tar header
7  *
8  *  References:
9  *    GNU tar and star man pages,
10  *    Opengroup's ustar interchange format,
11  *      http://www.opengroup.org/onlinepubs/007904975/utilities/pax.html
12  */
13
14 #include "libbb.h"
15 #include "unarchive.h"
16
17 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
18 static char *longname;
19 static char *linkname;
20 #else
21 enum {
22         longname = 0,
23         linkname = 0,
24 };
25 #endif
26
27 /* NB: _DESTROYS_ str[len] character! */
28 static unsigned long long getOctal(char *str, int len)
29 {
30         unsigned long long v;
31         /* Actually, tar header allows leading spaces also.
32          * Oh well, we will be liberal and skip this...
33          * The only downside probably is that we allow "-123" too :)
34         if (*str < '0' || *str > '7')
35                 bb_error_msg_and_die("corrupted octal value in tar header");
36         */
37         str[len] = '\0';
38         v = strtoull(str, &str, 8);
39         if (*str && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY || *str != ' '))
40                 bb_error_msg_and_die("corrupted octal value in tar header");
41         return v;
42 }
43 #define GET_OCTAL(a) getOctal((a), sizeof(a))
44
45 void BUG_tar_header_size(void);
46 char get_header_tar(archive_handle_t *archive_handle)
47 {
48         static smallint end;
49 #if ENABLE_FEATURE_TAR_AUTODETECT
50         static smallint not_first;
51 #endif
52
53         file_header_t *file_header = archive_handle->file_header;
54         struct {
55                 /* ustar header, Posix 1003.1 */
56                 char name[100];     /*   0-99 */
57                 char mode[8];       /* 100-107 */
58                 char uid[8];        /* 108-115 */
59                 char gid[8];        /* 116-123 */
60                 char size[12];      /* 124-135 */
61                 char mtime[12];     /* 136-147 */
62                 char chksum[8];     /* 148-155 */
63                 char typeflag;      /* 156-156 */
64                 char linkname[100]; /* 157-256 */
65                 /* POSIX:   "ustar" NUL "00" */
66                 /* GNU tar: "ustar  " NUL */
67                 /* Normally it's defined as magic[6] followed by
68                  * version[2], but we put them together to save code.
69                  */
70                 char magic[8];      /* 257-264 */
71                 char uname[32];     /* 265-296 */
72                 char gname[32];     /* 297-328 */
73                 char devmajor[8];   /* 329-336 */
74                 char devminor[8];   /* 337-344 */
75                 char prefix[155];   /* 345-499 */
76                 char padding[12];   /* 500-512 */
77         } tar;
78         char *cp;
79         int i, sum_u, sum;
80 #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
81         int sum_s;
82 #endif
83         int parse_names;
84
85         if (sizeof(tar) != 512)
86                 BUG_tar_header_size();
87
88 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
89  again:
90 #endif
91         /* Align header */
92         data_align(archive_handle, 512);
93
94  again_after_align:
95
96 #if ENABLE_DESKTOP
97         i = full_read(archive_handle->src_fd, &tar, 512);
98         /* if GNU tar sees EOF in above read, it says:
99          * "tar: A lone zero block at N", where N = kilobyte
100          * where EOF was met (not EOF block, actual EOF!),
101          * and tar will exit with error code 0.
102          * We will mimic exit(0), although we will not mimic
103          * the message and we don't check whether we indeed
104          * saw zero block directly before this. */
105         if (i == 0)
106                 xfunc_error_retval = 0;
107         if (i != 512)
108                 bb_error_msg_and_die("short read");
109 #else
110         xread(archive_handle->src_fd, &tar, 512);
111 #endif
112         archive_handle->offset += 512;
113
114         /* If there is no filename its an empty header */
115         if (tar.name[0] == 0 && tar.prefix[0] == 0) {
116                 if (end) {
117                         /* This is the second consecutive empty header! End of archive!
118                          * Read until the end to empty the pipe from gz or bz2
119                          */
120                         while (full_read(archive_handle->src_fd, &tar, 512) == 512)
121                                 continue;
122                         return EXIT_FAILURE;
123                 }
124                 end = 1;
125                 return EXIT_SUCCESS;
126         }
127         end = 0;
128
129         /* Check header has valid magic, "ustar" is for the proper tar,
130          * five NULs are for the old tar format  */
131         if (strncmp(tar.magic, "ustar", 5) != 0
132          && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
133              || memcmp(tar.magic, "\0\0\0\0", 5) != 0)
134         ) {
135 #if ENABLE_FEATURE_TAR_AUTODETECT
136                 char (*get_header_ptr)(archive_handle_t *);
137
138                 /* tar gz/bz autodetect: check for gz/bz2 magic.
139                  * If it is the very first block, and we see the magic,
140                  * we can switch to get_header_tar_gz/bz2/lzma().
141                  * Needs seekable fd. I wish recv(MSG_PEEK) would work
142                  * on any fd... */
143                 if (not_first)
144                         goto err;
145 #if ENABLE_FEATURE_TAR_GZIP
146                 if (tar.name[0] == 0x1f && tar.name[1] == 0x8b) { /* gzip */
147                         get_header_ptr = get_header_tar_gz;
148                 } else
149 #endif
150 #if ENABLE_FEATURE_TAR_BZIP2
151                 if (tar.name[0] == 'B' && tar.name[1] == 'Z'
152                  && tar.name[2] == 'h' && isdigit(tar.name[3])
153                 ) { /* bzip2 */
154                         get_header_ptr = get_header_tar_bz2;
155                 } else
156 #endif
157                         goto err;
158                 if (lseek(archive_handle->src_fd, -512, SEEK_CUR) != 0)
159                         goto err;
160                 while (get_header_ptr(archive_handle) == EXIT_SUCCESS)
161                         continue;
162                 return EXIT_FAILURE;
163  err:
164 #endif /* FEATURE_TAR_AUTODETECT */
165                 bb_error_msg_and_die("invalid tar magic");
166         }
167
168 #if ENABLE_FEATURE_TAR_AUTODETECT
169         not_first = 1;
170 #endif
171
172         /* Do checksum on headers.
173          * POSIX says that checksum is done on unsigned bytes, but
174          * Sun and HP-UX gets it wrong... more details in
175          * GNU tar source. */
176 #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
177         sum_s = ' ' * sizeof(tar.chksum);
178 #endif
179         sum_u = ' ' * sizeof(tar.chksum);
180         for (i = 0; i < 148; i++) {
181                 sum_u += ((unsigned char*)&tar)[i];
182 #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
183                 sum_s += ((signed char*)&tar)[i];
184 #endif
185         }
186         for (i = 156; i < 512; i++) {
187                 sum_u += ((unsigned char*)&tar)[i];
188 #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
189                 sum_s += ((signed char*)&tar)[i];
190 #endif
191         }
192 #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
193         sum = strtoul(tar.chksum, &cp, 8);
194         if ((*cp && *cp != ' ')
195          || (sum_u != sum USE_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum))
196         ) {
197                 bb_error_msg_and_die("invalid tar header checksum");
198         }
199 #else
200         /* This field does not need special treatment (getOctal) */
201         sum = xstrtoul(tar.chksum, 8);
202         if (sum_u != sum USE_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) {
203                 bb_error_msg_and_die("invalid tar header checksum");
204         }
205 #endif
206
207         /* 0 is reserved for high perf file, treat as normal file */
208         if (!tar.typeflag) tar.typeflag = '0';
209         parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7');
210
211         /* getOctal trashes subsequent field, therefore we call it
212          * on fields in reverse order */
213         if (tar.devmajor[0]) {
214                 char t = tar.prefix[0];
215                 /* we trash prefix[0] here, but we DO need it later! */
216                 unsigned minor = GET_OCTAL(tar.devminor);
217                 unsigned major = GET_OCTAL(tar.devmajor);
218                 file_header->device = makedev(major, minor);
219                 tar.prefix[0] = t;
220         }
221         file_header->link_target = NULL;
222         if (!linkname && parse_names && tar.linkname[0]) {
223                 /* we trash magic[0] here, it's ok */
224                 tar.linkname[sizeof(tar.linkname)] = '\0';
225                 file_header->link_target = xstrdup(tar.linkname);
226                 /* FIXME: what if we have non-link object with link_target? */
227                 /* Will link_target be free()ed? */
228         }
229 #if ENABLE_FEATURE_TAR_UNAME_GNAME
230         file_header->uname = tar.uname[0] ? xstrndup(tar.uname, sizeof(tar.uname)) : NULL;
231         file_header->gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL;
232 #endif
233         file_header->mtime = GET_OCTAL(tar.mtime);
234         file_header->size = GET_OCTAL(tar.size);
235         file_header->gid = GET_OCTAL(tar.gid);
236         file_header->uid = GET_OCTAL(tar.uid);
237         /* Set bits 0-11 of the files mode */
238         file_header->mode = 07777 & GET_OCTAL(tar.mode);
239
240         file_header->name = NULL;
241         if (!longname && parse_names) {
242                 /* we trash mode[0] here, it's ok */
243                 tar.name[sizeof(tar.name)] = '\0';
244                 if (tar.prefix[0]) {
245                         /* and padding[0] */
246                         tar.prefix[sizeof(tar.prefix)] = '\0';
247                         file_header->name = concat_path_file(tar.prefix, tar.name);
248                 } else
249                         file_header->name = xstrdup(tar.name);
250         }
251
252         /* Set bits 12-15 of the files mode */
253         /* (typeflag was not trashed because chksum does not use getOctal) */
254         switch (tar.typeflag) {
255         /* busybox identifies hard links as being regular files with 0 size and a link name */
256         case '1':
257                 file_header->mode |= S_IFREG;
258                 break;
259         case '7':
260         /* case 0: */
261         case '0':
262 #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
263                 if (last_char_is(file_header->name, '/')) {
264                         file_header->mode |= S_IFDIR;
265                 } else
266 #endif
267                 file_header->mode |= S_IFREG;
268                 break;
269         case '2':
270                 file_header->mode |= S_IFLNK;
271                 break;
272         case '3':
273                 file_header->mode |= S_IFCHR;
274                 break;
275         case '4':
276                 file_header->mode |= S_IFBLK;
277                 break;
278         case '5':
279                 file_header->mode |= S_IFDIR;
280                 break;
281         case '6':
282                 file_header->mode |= S_IFIFO;
283                 break;
284 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
285         case 'L':
286                 /* free: paranoia: tar with several consecutive longnames */
287                 free(longname);
288                 /* For paranoia reasons we allocate extra NUL char */
289                 longname = xzalloc(file_header->size + 1);
290                 /* We read ASCIZ string, including NUL */
291                 xread(archive_handle->src_fd, longname, file_header->size);
292                 archive_handle->offset += file_header->size;
293                 /* return get_header_tar(archive_handle); */
294                 /* gcc 4.1.1 didn't optimize it into jump */
295                 /* so we will do it ourself, this also saves stack */
296                 goto again;
297         case 'K':
298                 free(linkname);
299                 linkname = xzalloc(file_header->size + 1);
300                 xread(archive_handle->src_fd, linkname, file_header->size);
301                 archive_handle->offset += file_header->size;
302                 /* return get_header_tar(archive_handle); */
303                 goto again;
304         case 'D':       /* GNU dump dir */
305         case 'M':       /* Continuation of multi volume archive */
306         case 'N':       /* Old GNU for names > 100 characters */
307         case 'S':       /* Sparse file */
308         case 'V':       /* Volume header */
309 #endif
310         case 'g':       /* pax global header */
311         case 'x': {     /* pax extended header */
312                 off_t sz;
313                 bb_error_msg("warning: skipping header '%c'", tar.typeflag);
314                 sz = (file_header->size + 511) & ~(off_t)511;
315                 archive_handle->offset += sz;
316                 sz >>= 9; /* sz /= 512 but w/o contortions for signed div */
317                 while (sz--)
318                         xread(archive_handle->src_fd, &tar, 512);
319                 /* return get_header_tar(archive_handle); */
320                 goto again_after_align;
321         }
322         default:
323                 bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag);
324         }
325
326 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
327         if (longname) {
328                 file_header->name = longname;
329                 longname = NULL;
330         }
331         if (linkname) {
332                 file_header->link_target = linkname;
333                 linkname = NULL;
334         }
335 #endif
336         if (!strncmp(file_header->name, "/../"+1, 3)
337          || strstr(file_header->name, "/../")
338         ) {
339                 bb_error_msg_and_die("name with '..' encountered: '%s'",
340                                 file_header->name);
341         }
342
343         /* Strip trailing '/' in directories */
344         /* Must be done after mode is set as '/' is used to check if it's a directory */
345         cp = last_char_is(file_header->name, '/');
346
347         if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
348                 archive_handle->action_header(archive_handle->file_header);
349                 /* Note that we kill the '/' only after action_header() */
350                 /* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */
351                 if (cp) *cp = '\0';
352                 archive_handle->flags |= ARCHIVE_EXTRACT_QUIET;
353                 archive_handle->action_data(archive_handle);
354                 llist_add_to(&(archive_handle->passed), file_header->name);
355         } else {
356                 data_skip(archive_handle);
357                 free(file_header->name);
358         }
359         archive_handle->offset += file_header->size;
360
361         free(file_header->link_target);
362         /* Do not free(file_header->name)! */
363 #if ENABLE_FEATURE_TAR_UNAME_GNAME
364         free(file_header->uname);
365         free(file_header->gname);
366 #endif
367         return EXIT_SUCCESS;
368 }