[1765] | 1 | /* vi: set sw=4 ts=4: */
|
---|
[821] | 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 |
|
---|
[1765] | 14 | #include "libbb.h"
|
---|
[821] | 15 | #include "unarchive.h"
|
---|
| 16 |
|
---|
[1765] | 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 | };
|
---|
[821] | 25 | #endif
|
---|
| 26 |
|
---|
[1765] | 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);
|
---|
[821] | 46 | char get_header_tar(archive_handle_t *archive_handle)
|
---|
| 47 | {
|
---|
[1765] | 48 | static smallint end;
|
---|
| 49 |
|
---|
[821] | 50 | file_header_t *file_header = archive_handle->file_header;
|
---|
[1765] | 51 | struct {
|
---|
[821] | 52 | /* ustar header, Posix 1003.1 */
|
---|
[1765] | 53 | char name[100]; /* 0-99 */
|
---|
| 54 | char mode[8]; /* 100-107 */
|
---|
| 55 | char uid[8]; /* 108-115 */
|
---|
| 56 | char gid[8]; /* 116-123 */
|
---|
| 57 | char size[12]; /* 124-135 */
|
---|
| 58 | char mtime[12]; /* 136-147 */
|
---|
| 59 | char chksum[8]; /* 148-155 */
|
---|
| 60 | char typeflag; /* 156-156 */
|
---|
| 61 | char linkname[100]; /* 157-256 */
|
---|
| 62 | char magic[6]; /* 257-262 */
|
---|
| 63 | char version[2]; /* 263-264 */
|
---|
| 64 | char uname[32]; /* 265-296 */
|
---|
| 65 | char gname[32]; /* 297-328 */
|
---|
| 66 | char devmajor[8]; /* 329-336 */
|
---|
| 67 | char devminor[8]; /* 337-344 */
|
---|
| 68 | char prefix[155]; /* 345-499 */
|
---|
| 69 | char padding[12]; /* 500-512 */
|
---|
[821] | 70 | } tar;
|
---|
[1765] | 71 | char *cp;
|
---|
| 72 | int i, sum_u, sum;
|
---|
| 73 | #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
|
---|
| 74 | int sum_s;
|
---|
| 75 | #endif
|
---|
| 76 | int parse_names;
|
---|
[821] | 77 |
|
---|
[1765] | 78 | if (sizeof(tar) != 512)
|
---|
| 79 | BUG_tar_header_size();
|
---|
| 80 |
|
---|
| 81 | #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
|
---|
| 82 | again:
|
---|
| 83 | #endif
|
---|
[821] | 84 | /* Align header */
|
---|
| 85 | data_align(archive_handle, 512);
|
---|
| 86 |
|
---|
[1765] | 87 | again_after_align:
|
---|
| 88 |
|
---|
| 89 | xread(archive_handle->src_fd, &tar, 512);
|
---|
[821] | 90 | archive_handle->offset += 512;
|
---|
| 91 |
|
---|
| 92 | /* If there is no filename its an empty header */
|
---|
[1765] | 93 | if (tar.name[0] == 0) {
|
---|
[821] | 94 | if (end) {
|
---|
| 95 | /* This is the second consecutive empty header! End of archive!
|
---|
| 96 | * Read until the end to empty the pipe from gz or bz2
|
---|
| 97 | */
|
---|
[1765] | 98 | while (full_read(archive_handle->src_fd, &tar, 512) == 512)
|
---|
| 99 | /* repeat */;
|
---|
| 100 | return EXIT_FAILURE;
|
---|
[821] | 101 | }
|
---|
| 102 | end = 1;
|
---|
[1765] | 103 | return EXIT_SUCCESS;
|
---|
[821] | 104 | }
|
---|
| 105 | end = 0;
|
---|
| 106 |
|
---|
| 107 | /* Check header has valid magic, "ustar" is for the proper tar
|
---|
| 108 | * 0's are for the old tar format
|
---|
| 109 | */
|
---|
[1765] | 110 | if (strncmp(tar.magic, "ustar", 5) != 0) {
|
---|
| 111 | #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
|
---|
| 112 | if (memcmp(tar.magic, "\0\0\0\0", 5) != 0)
|
---|
[821] | 113 | #endif
|
---|
[1765] | 114 | bb_error_msg_and_die("invalid tar magic");
|
---|
[821] | 115 | }
|
---|
[1765] | 116 |
|
---|
| 117 | /* Do checksum on headers.
|
---|
| 118 | * POSIX says that checksum is done on unsigned bytes, but
|
---|
| 119 | * Sun and HP-UX gets it wrong... more details in
|
---|
| 120 | * GNU tar source. */
|
---|
| 121 | #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
|
---|
| 122 | sum_s = ' ' * sizeof(tar.chksum);
|
---|
| 123 | #endif
|
---|
| 124 | sum_u = ' ' * sizeof(tar.chksum);
|
---|
| 125 | for (i = 0; i < 148; i++) {
|
---|
| 126 | sum_u += ((unsigned char*)&tar)[i];
|
---|
| 127 | #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
|
---|
| 128 | sum_s += ((signed char*)&tar)[i];
|
---|
| 129 | #endif
|
---|
[821] | 130 | }
|
---|
[1765] | 131 | for (i = 156; i < 512; i++) {
|
---|
| 132 | sum_u += ((unsigned char*)&tar)[i];
|
---|
| 133 | #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
|
---|
| 134 | sum_s += ((signed char*)&tar)[i];
|
---|
| 135 | #endif
|
---|
[821] | 136 | }
|
---|
[1765] | 137 | #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
|
---|
| 138 | sum = strtoul(tar.chksum, &cp, 8);
|
---|
| 139 | if ((*cp && *cp != ' ')
|
---|
| 140 | || (sum_u != sum USE_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum))
|
---|
| 141 | ) {
|
---|
| 142 | bb_error_msg_and_die("invalid tar header checksum");
|
---|
[821] | 143 | }
|
---|
[1765] | 144 | #else
|
---|
| 145 | /* This field does not need special treatment (getOctal) */
|
---|
| 146 | sum = xstrtoul(tar.chksum, 8);
|
---|
| 147 | if (sum_u != sum USE_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) {
|
---|
| 148 | bb_error_msg_and_die("invalid tar header checksum");
|
---|
[821] | 149 | }
|
---|
| 150 | #endif
|
---|
| 151 |
|
---|
[1765] | 152 | /* 0 is reserved for high perf file, treat as normal file */
|
---|
| 153 | if (!tar.typeflag) tar.typeflag = '0';
|
---|
| 154 | parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7');
|
---|
| 155 |
|
---|
| 156 | /* getOctal trashes subsequent field, therefore we call it
|
---|
| 157 | * on fields in reverse order */
|
---|
| 158 | if (tar.devmajor[0]) {
|
---|
| 159 | unsigned minor = GET_OCTAL(tar.devminor);
|
---|
| 160 | unsigned major = GET_OCTAL(tar.devmajor);
|
---|
| 161 | file_header->device = makedev(major, minor);
|
---|
[821] | 162 | }
|
---|
[1765] | 163 | file_header->link_target = NULL;
|
---|
| 164 | if (!linkname && parse_names && tar.linkname[0]) {
|
---|
| 165 | /* we trash magic[0] here, it's ok */
|
---|
| 166 | tar.linkname[sizeof(tar.linkname)] = '\0';
|
---|
| 167 | file_header->link_target = xstrdup(tar.linkname);
|
---|
| 168 | /* FIXME: what if we have non-link object with link_target? */
|
---|
| 169 | /* Will link_target be free()ed? */
|
---|
| 170 | }
|
---|
| 171 | file_header->mtime = GET_OCTAL(tar.mtime);
|
---|
| 172 | file_header->size = GET_OCTAL(tar.size);
|
---|
| 173 | file_header->gid = GET_OCTAL(tar.gid);
|
---|
| 174 | file_header->uid = GET_OCTAL(tar.uid);
|
---|
| 175 | /* Set bits 0-11 of the files mode */
|
---|
| 176 | file_header->mode = 07777 & GET_OCTAL(tar.mode);
|
---|
[821] | 177 |
|
---|
[1765] | 178 | file_header->name = NULL;
|
---|
| 179 | if (!longname && parse_names) {
|
---|
| 180 | /* we trash mode[0] here, it's ok */
|
---|
| 181 | tar.name[sizeof(tar.name)] = '\0';
|
---|
| 182 | if (tar.prefix[0]) {
|
---|
| 183 | /* and padding[0] */
|
---|
| 184 | tar.prefix[sizeof(tar.prefix)] = '\0';
|
---|
| 185 | file_header->name = concat_path_file(tar.prefix, tar.name);
|
---|
| 186 | } else
|
---|
| 187 | file_header->name = xstrdup(tar.name);
|
---|
| 188 | }
|
---|
[821] | 189 |
|
---|
| 190 | /* Set bits 12-15 of the files mode */
|
---|
[1765] | 191 | /* (typeflag was not trashed because chksum does not use getOctal) */
|
---|
| 192 | switch (tar.typeflag) {
|
---|
[821] | 193 | /* busybox identifies hard links as being regular files with 0 size and a link name */
|
---|
| 194 | case '1':
|
---|
| 195 | file_header->mode |= S_IFREG;
|
---|
| 196 | break;
|
---|
| 197 | case '7':
|
---|
[1765] | 198 | /* case 0: */
|
---|
[821] | 199 | case '0':
|
---|
[1765] | 200 | #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
|
---|
[821] | 201 | if (last_char_is(file_header->name, '/')) {
|
---|
| 202 | file_header->mode |= S_IFDIR;
|
---|
| 203 | } else
|
---|
| 204 | #endif
|
---|
[1765] | 205 | file_header->mode |= S_IFREG;
|
---|
[821] | 206 | break;
|
---|
| 207 | case '2':
|
---|
| 208 | file_header->mode |= S_IFLNK;
|
---|
| 209 | break;
|
---|
| 210 | case '3':
|
---|
| 211 | file_header->mode |= S_IFCHR;
|
---|
| 212 | break;
|
---|
| 213 | case '4':
|
---|
| 214 | file_header->mode |= S_IFBLK;
|
---|
| 215 | break;
|
---|
| 216 | case '5':
|
---|
| 217 | file_header->mode |= S_IFDIR;
|
---|
| 218 | break;
|
---|
| 219 | case '6':
|
---|
| 220 | file_header->mode |= S_IFIFO;
|
---|
| 221 | break;
|
---|
[1765] | 222 | #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
|
---|
| 223 | case 'L':
|
---|
| 224 | /* free: paranoia: tar with several consecutive longnames */
|
---|
| 225 | free(longname);
|
---|
| 226 | /* For paranoia reasons we allocate extra NUL char */
|
---|
| 227 | longname = xzalloc(file_header->size + 1);
|
---|
| 228 | /* We read ASCIZ string, including NUL */
|
---|
| 229 | xread(archive_handle->src_fd, longname, file_header->size);
|
---|
| 230 | archive_handle->offset += file_header->size;
|
---|
| 231 | /* return get_header_tar(archive_handle); */
|
---|
| 232 | /* gcc 4.1.1 didn't optimize it into jump */
|
---|
| 233 | /* so we will do it ourself, this also saves stack */
|
---|
| 234 | goto again;
|
---|
| 235 | case 'K':
|
---|
| 236 | free(linkname);
|
---|
| 237 | linkname = xzalloc(file_header->size + 1);
|
---|
| 238 | xread(archive_handle->src_fd, linkname, file_header->size);
|
---|
| 239 | archive_handle->offset += file_header->size;
|
---|
| 240 | /* return get_header_tar(archive_handle); */
|
---|
| 241 | goto again;
|
---|
[821] | 242 | case 'D': /* GNU dump dir */
|
---|
[1765] | 243 | case 'M': /* Continuation of multi volume archive */
|
---|
[821] | 244 | case 'N': /* Old GNU for names > 100 characters */
|
---|
| 245 | case 'S': /* Sparse file */
|
---|
| 246 | case 'V': /* Volume header */
|
---|
| 247 | #endif
|
---|
| 248 | case 'g': /* pax global header */
|
---|
[1765] | 249 | case 'x': { /* pax extended header */
|
---|
| 250 | off_t sz;
|
---|
| 251 | bb_error_msg("warning: skipping header '%c'", tar.typeflag);
|
---|
| 252 | sz = (file_header->size + 511) & ~(off_t)511;
|
---|
| 253 | archive_handle->offset += sz;
|
---|
| 254 | sz >>= 9; /* sz /= 512 but w/o contortions for signed div */
|
---|
| 255 | while (sz--)
|
---|
| 256 | xread(archive_handle->src_fd, &tar, 512);
|
---|
| 257 | /* return get_header_tar(archive_handle); */
|
---|
| 258 | goto again_after_align;
|
---|
| 259 | }
|
---|
[821] | 260 | default:
|
---|
[1765] | 261 | bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag);
|
---|
[821] | 262 | }
|
---|
[1765] | 263 |
|
---|
| 264 | #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
|
---|
| 265 | if (longname) {
|
---|
| 266 | file_header->name = longname;
|
---|
| 267 | longname = NULL;
|
---|
[821] | 268 | }
|
---|
[1765] | 269 | if (linkname) {
|
---|
| 270 | file_header->link_target = linkname;
|
---|
| 271 | linkname = NULL;
|
---|
| 272 | }
|
---|
| 273 | #endif
|
---|
| 274 | if (!strncmp(file_header->name, "/../"+1, 3)
|
---|
| 275 | || strstr(file_header->name, "/../")
|
---|
| 276 | ) {
|
---|
| 277 | bb_error_msg_and_die("name with '..' encountered: '%s'",
|
---|
| 278 | file_header->name);
|
---|
| 279 | }
|
---|
[821] | 280 |
|
---|
[1765] | 281 | /* Strip trailing '/' in directories */
|
---|
| 282 | /* Must be done after mode is set as '/' is used to check if it's a directory */
|
---|
| 283 | cp = last_char_is(file_header->name, '/');
|
---|
| 284 |
|
---|
[821] | 285 | if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
|
---|
| 286 | archive_handle->action_header(archive_handle->file_header);
|
---|
[1765] | 287 | /* Note that we kill the '/' only after action_header() */
|
---|
| 288 | /* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */
|
---|
| 289 | if (cp) *cp = '\0';
|
---|
[821] | 290 | archive_handle->flags |= ARCHIVE_EXTRACT_QUIET;
|
---|
| 291 | archive_handle->action_data(archive_handle);
|
---|
| 292 | llist_add_to(&(archive_handle->passed), file_header->name);
|
---|
| 293 | } else {
|
---|
| 294 | data_skip(archive_handle);
|
---|
[1765] | 295 | free(file_header->name);
|
---|
[821] | 296 | }
|
---|
| 297 | archive_handle->offset += file_header->size;
|
---|
| 298 |
|
---|
[1765] | 299 | free(file_header->link_target);
|
---|
| 300 | /* Do not free(file_header->name)! */
|
---|
[821] | 301 |
|
---|
[1765] | 302 | return EXIT_SUCCESS;
|
---|
[821] | 303 | }
|
---|