Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/archival/libunarchive/get_header_tar.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/archival/libunarchive/get_header_tar.c
r821 r1770 1 /* vi: set sw=4 ts=4: */ 1 2 /* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 2 3 * … … 11 12 */ 12 13 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <sys/sysmacros.h> /* For makedev */ 14 #include "libbb.h" 17 15 #include "unarchive.h" 18 #include "libbb.h" 19 20 #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS 21 static char *longname = NULL; 22 static char *linkname = NULL; 23 #endif 24 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); 25 46 char get_header_tar(archive_handle_t *archive_handle) 26 47 { 48 static smallint end; 49 27 50 file_header_t *file_header = archive_handle->file_header; 28 union{51 struct { 29 52 /* ustar header, Posix 1003.1 */ 30 unsigned char raw[512]; 31 struct { 32 char name[100]; /* 0-99 */ 33 char mode[8]; /* 100-107 */ 34 char uid[8]; /* 108-115 */ 35 char gid[8]; /* 116-123 */ 36 char size[12]; /* 124-135 */ 37 char mtime[12]; /* 136-147 */ 38 char chksum[8]; /* 148-155 */ 39 char typeflag; /* 156-156 */ 40 char linkname[100]; /* 157-256 */ 41 char magic[6]; /* 257-262 */ 42 char version[2]; /* 263-264 */ 43 char uname[32]; /* 265-296 */ 44 char gname[32]; /* 297-328 */ 45 char devmajor[8]; /* 329-336 */ 46 char devminor[8]; /* 337-344 */ 47 char prefix[155]; /* 345-499 */ 48 char padding[12]; /* 500-512 */ 49 } formated; 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 */ 50 70 } tar; 51 long sum = 0; 52 long i; 53 static int end = 0; 54 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; 77 78 if (sizeof(tar) != 512) 79 BUG_tar_header_size(); 80 81 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 82 again: 83 #endif 55 84 /* Align header */ 56 85 data_align(archive_handle, 512); 57 86 58 if (bb_full_read(archive_handle->src_fd, tar.raw, 512) != 512) { 59 /* Assume end of file */ 60 bb_error_msg_and_die("Short header"); 61 //return(EXIT_FAILURE); 62 } 87 again_after_align: 88 89 xread(archive_handle->src_fd, &tar, 512); 63 90 archive_handle->offset += 512; 64 91 65 92 /* If there is no filename its an empty header */ 66 if (tar. formated.name[0] == 0) {93 if (tar.name[0] == 0) { 67 94 if (end) { 68 95 /* This is the second consecutive empty header! End of archive! 69 96 * Read until the end to empty the pipe from gz or bz2 70 97 */ 71 while (bb_full_read(archive_handle->src_fd, tar.raw, 512) == 512); 72 return(EXIT_FAILURE); 98 while (full_read(archive_handle->src_fd, &tar, 512) == 512) 99 /* repeat */; 100 return EXIT_FAILURE; 73 101 } 74 102 end = 1; 75 return (EXIT_SUCCESS);103 return EXIT_SUCCESS; 76 104 } 77 105 end = 0; … … 80 108 * 0's are for the old tar format 81 109 */ 82 if (strncmp(tar.formated.magic, "ustar", 5) != 0) { 83 #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY 84 if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0) 85 #endif 86 bb_error_msg_and_die("Invalid tar magic"); 87 } 88 /* Do checksum on headers */ 89 for (i = 0; i < 148 ; i++) { 90 sum += tar.raw[i]; 91 } 92 sum += ' ' * 8; 93 for (i = 156; i < 512 ; i++) { 94 sum += tar.raw[i]; 95 } 96 if (sum != strtol(tar.formated.chksum, NULL, 8)) { 97 bb_error_msg("Invalid tar header checksum"); 98 return(EXIT_FAILURE); 99 } 100 101 #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS 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) 113 #endif 114 bb_error_msg_and_die("invalid tar magic"); 115 } 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 130 } 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 136 } 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"); 143 } 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"); 149 } 150 #endif 151 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); 162 } 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); 177 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 } 189 190 /* Set bits 12-15 of the files mode */ 191 /* (typeflag was not trashed because chksum does not use getOctal) */ 192 switch (tar.typeflag) { 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': 198 /* case 0: */ 199 case '0': 200 #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY 201 if (last_char_is(file_header->name, '/')) { 202 file_header->mode |= S_IFDIR; 203 } else 204 #endif 205 file_header->mode |= S_IFREG; 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; 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; 242 case 'D': /* GNU dump dir */ 243 case 'M': /* Continuation of multi volume archive */ 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 */ 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 } 260 default: 261 bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag); 262 } 263 264 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 102 265 if (longname) { 103 266 file_header->name = longname; 104 267 longname = NULL; 105 268 } 106 elseif (linkname) {107 file_header-> name= linkname;269 if (linkname) { 270 file_header->link_target = linkname; 108 271 linkname = NULL; 109 } else 110 #endif 111 { 112 file_header->name = bb_xstrndup(tar.formated.name,100); 113 114 if (tar.formated.prefix[0]) { 115 char *temp = file_header->name; 116 file_header->name = concat_path_file(tar.formated.prefix, temp); 117 free(temp); 118 } 119 } 120 121 file_header->uid = strtol(tar.formated.uid, NULL, 8); 122 file_header->gid = strtol(tar.formated.gid, NULL, 8); 123 file_header->size = strtol(tar.formated.size, NULL, 8); 124 file_header->mtime = strtol(tar.formated.mtime, NULL, 8); 125 file_header->link_name = (tar.formated.linkname[0] != '\0') ? 126 bb_xstrdup(tar.formated.linkname) : NULL; 127 file_header->device = makedev(strtol(tar.formated.devmajor, NULL, 8), 128 strtol(tar.formated.devminor, NULL, 8)); 129 130 /* Set bits 0-11 of the files mode */ 131 file_header->mode = 07777 & strtol(tar.formated.mode, NULL, 8); 132 133 /* Set bits 12-15 of the files mode */ 134 switch (tar.formated.typeflag) { 135 /* busybox identifies hard links as being regular files with 0 size and a link name */ 136 case '1': 137 file_header->mode |= S_IFREG; 138 break; 139 case '7': 140 /* Reserved for high performance files, treat as normal file */ 141 case 0: 142 case '0': 143 #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY 144 if (last_char_is(file_header->name, '/')) { 145 file_header->mode |= S_IFDIR; 146 } else 147 #endif 148 file_header->mode |= S_IFREG; 149 break; 150 case '2': 151 file_header->mode |= S_IFLNK; 152 break; 153 case '3': 154 file_header->mode |= S_IFCHR; 155 break; 156 case '4': 157 file_header->mode |= S_IFBLK; 158 break; 159 case '5': 160 file_header->mode |= S_IFDIR; 161 break; 162 case '6': 163 file_header->mode |= S_IFIFO; 164 break; 165 #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS 166 case 'L': { 167 longname = xzalloc(file_header->size + 1); 168 archive_xread_all(archive_handle, longname, file_header->size); 169 archive_handle->offset += file_header->size; 170 171 return(get_header_tar(archive_handle)); 172 } 173 case 'K': { 174 linkname = xzalloc(file_header->size + 1); 175 archive_xread_all(archive_handle, linkname, file_header->size); 176 archive_handle->offset += file_header->size; 177 178 file_header->name = linkname; 179 return(get_header_tar(archive_handle)); 180 } 181 case 'D': /* GNU dump dir */ 182 case 'M': /* Continuation of multi volume archive*/ 183 case 'N': /* Old GNU for names > 100 characters */ 184 case 'S': /* Sparse file */ 185 case 'V': /* Volume header */ 186 #endif 187 case 'g': /* pax global header */ 188 case 'x': /* pax extended header */ 189 bb_error_msg("Ignoring extension type %c", tar.formated.typeflag); 190 break; 191 default: 192 bb_error_msg("Unknown typeflag: 0x%x", tar.formated.typeflag); 193 } 194 { /* Strip trailing '/' in directories */ 195 /* Must be done after mode is set as '/' is used to check if its a directory */ 196 char *tmp = last_char_is(file_header->name, '/'); 197 if (tmp) { 198 *tmp = '\0'; 199 } 200 } 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 } 280 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, '/'); 201 284 202 285 if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { 203 286 archive_handle->action_header(archive_handle->file_header); 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'; 204 290 archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; 205 291 archive_handle->action_data(archive_handle); … … 207 293 } else { 208 294 data_skip(archive_handle); 295 free(file_header->name); 209 296 } 210 297 archive_handle->offset += file_header->size; 211 298 212 free(file_header->link_name); 213 214 return(EXIT_SUCCESS); 299 free(file_header->link_target); 300 /* Do not free(file_header->name)! */ 301 302 return EXIT_SUCCESS; 215 303 }
Note:
See TracChangeset
for help on using the changeset viewer.