diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-11-24 14:51:01 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-11-24 14:51:01 +0000 |
commit | 376ce1e775a97a01f1c454497fbe34d326043328 (patch) | |
tree | 94f9c05606bc4b38b47d408c7cf5e45be9ec29ec /archival/libunarchive | |
parent | 14621929a194f9c8e618efdd8f58a031f7351231 (diff) | |
download | busybox-w32-376ce1e775a97a01f1c454497fbe34d326043328.tar.gz busybox-w32-376ce1e775a97a01f1c454497fbe34d326043328.tar.bz2 busybox-w32-376ce1e775a97a01f1c454497fbe34d326043328.zip |
tar:
* unpack: handle tar header fields which are not NUL terminated
* pack: handle 4+GB files correctly
* pack: refuse to store 101+ softlinks (was truncating link
target name)
* pack: mask mode with 07777
Diffstat (limited to 'archival/libunarchive')
-rw-r--r-- | archival/libunarchive/get_header_tar.c | 167 |
1 files changed, 93 insertions, 74 deletions
diff --git a/archival/libunarchive/get_header_tar.c b/archival/libunarchive/get_header_tar.c index f78377e28..b5cae9f12 100644 --- a/archival/libunarchive/get_header_tar.c +++ b/archival/libunarchive/get_header_tar.c | |||
@@ -19,49 +19,69 @@ static char *longname = NULL; | |||
19 | static char *linkname = NULL; | 19 | static char *linkname = NULL; |
20 | #endif | 20 | #endif |
21 | 21 | ||
22 | /* NB: _DESTROYS_ str[len] character! */ | ||
23 | static unsigned long long getOctal(char *str, int len) | ||
24 | { | ||
25 | unsigned long long v; | ||
26 | /* Actually, tar header allows leading spaces also. | ||
27 | * Oh well, we will be liberal and skip this... | ||
28 | * The only downside probably is that we allow "-123" too :) | ||
29 | if (*str < '0' || *str > '7') | ||
30 | bb_error_msg_and_die("corrupted octal value in tar header"); | ||
31 | */ | ||
32 | str[len] = '\0'; | ||
33 | v = strtoull(str, &str, 8); | ||
34 | if (*str) | ||
35 | bb_error_msg_and_die("corrupted octal value in tar header"); | ||
36 | return v; | ||
37 | } | ||
38 | |||
39 | void BUG_tar_header_size(void); | ||
22 | char get_header_tar(archive_handle_t *archive_handle) | 40 | char get_header_tar(archive_handle_t *archive_handle) |
23 | { | 41 | { |
42 | static int end = 0; | ||
43 | |||
24 | file_header_t *file_header = archive_handle->file_header; | 44 | file_header_t *file_header = archive_handle->file_header; |
25 | union { | 45 | struct { |
26 | /* ustar header, Posix 1003.1 */ | 46 | /* ustar header, Posix 1003.1 */ |
27 | unsigned char raw[512]; | 47 | char name[100]; /* 0-99 */ |
28 | struct { | 48 | char mode[8]; /* 100-107 */ |
29 | char name[100]; /* 0-99 */ | 49 | char uid[8]; /* 108-115 */ |
30 | char mode[8]; /* 100-107 */ | 50 | char gid[8]; /* 116-123 */ |
31 | char uid[8]; /* 108-115 */ | 51 | char size[12]; /* 124-135 */ |
32 | char gid[8]; /* 116-123 */ | 52 | char mtime[12]; /* 136-147 */ |
33 | char size[12]; /* 124-135 */ | 53 | char chksum[8]; /* 148-155 */ |
34 | char mtime[12]; /* 136-147 */ | 54 | char typeflag; /* 156-156 */ |
35 | char chksum[8]; /* 148-155 */ | 55 | char linkname[100]; /* 157-256 */ |
36 | char typeflag; /* 156-156 */ | 56 | char magic[6]; /* 257-262 */ |
37 | char linkname[100]; /* 157-256 */ | 57 | char version[2]; /* 263-264 */ |
38 | char magic[6]; /* 257-262 */ | 58 | char uname[32]; /* 265-296 */ |
39 | char version[2]; /* 263-264 */ | 59 | char gname[32]; /* 297-328 */ |
40 | char uname[32]; /* 265-296 */ | 60 | char devmajor[8]; /* 329-336 */ |
41 | char gname[32]; /* 297-328 */ | 61 | char devminor[8]; /* 337-344 */ |
42 | char devmajor[8]; /* 329-336 */ | 62 | char prefix[155]; /* 345-499 */ |
43 | char devminor[8]; /* 337-344 */ | 63 | char padding[12]; /* 500-512 */ |
44 | char prefix[155]; /* 345-499 */ | ||
45 | char padding[12]; /* 500-512 */ | ||
46 | } formatted; | ||
47 | } tar; | 64 | } tar; |
48 | long sum = 0; | 65 | char *cp; |
49 | long i; | 66 | int sum, i; |
50 | static int end = 0; | 67 | |
68 | if (sizeof(tar) != 512) | ||
69 | BUG_tar_header_size(); | ||
51 | 70 | ||
52 | /* Align header */ | 71 | /* Align header */ |
53 | data_align(archive_handle, 512); | 72 | data_align(archive_handle, 512); |
54 | 73 | ||
55 | xread(archive_handle->src_fd, tar.raw, 512); | 74 | xread(archive_handle->src_fd, &tar, 512); |
56 | archive_handle->offset += 512; | 75 | archive_handle->offset += 512; |
57 | 76 | ||
58 | /* If there is no filename its an empty header */ | 77 | /* If there is no filename its an empty header */ |
59 | if (tar.formatted.name[0] == 0) { | 78 | if (tar.name[0] == 0) { |
60 | if (end) { | 79 | if (end) { |
61 | /* This is the second consecutive empty header! End of archive! | 80 | /* This is the second consecutive empty header! End of archive! |
62 | * Read until the end to empty the pipe from gz or bz2 | 81 | * Read until the end to empty the pipe from gz or bz2 |
63 | */ | 82 | */ |
64 | while (full_read(archive_handle->src_fd, tar.raw, 512) == 512); | 83 | while (full_read(archive_handle->src_fd, &tar, 512) == 512) |
84 | /* repeat */; | ||
65 | return EXIT_FAILURE; | 85 | return EXIT_FAILURE; |
66 | } | 86 | } |
67 | end = 1; | 87 | end = 1; |
@@ -72,21 +92,22 @@ char get_header_tar(archive_handle_t *archive_handle) | |||
72 | /* Check header has valid magic, "ustar" is for the proper tar | 92 | /* Check header has valid magic, "ustar" is for the proper tar |
73 | * 0's are for the old tar format | 93 | * 0's are for the old tar format |
74 | */ | 94 | */ |
75 | if (strncmp(tar.formatted.magic, "ustar", 5) != 0) { | 95 | if (strncmp(tar.magic, "ustar", 5) != 0) { |
76 | #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY | 96 | #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY |
77 | if (memcmp(tar.formatted.magic, "\0\0\0\0", 5) != 0) | 97 | if (memcmp(tar.magic, "\0\0\0\0", 5) != 0) |
78 | #endif | 98 | #endif |
79 | bb_error_msg_and_die("invalid tar magic"); | 99 | bb_error_msg_and_die("invalid tar magic"); |
80 | } | 100 | } |
81 | /* Do checksum on headers */ | 101 | /* Do checksum on headers */ |
102 | sum = ' ' * sizeof(tar.chksum); | ||
82 | for (i = 0; i < 148 ; i++) { | 103 | for (i = 0; i < 148 ; i++) { |
83 | sum += tar.raw[i]; | 104 | sum += ((char*)&tar)[i]; |
84 | } | 105 | } |
85 | sum += ' ' * 8; | ||
86 | for (i = 156; i < 512 ; i++) { | 106 | for (i = 156; i < 512 ; i++) { |
87 | sum += tar.raw[i]; | 107 | sum += ((char*)&tar)[i]; |
88 | } | 108 | } |
89 | if (sum != xstrtoul(tar.formatted.chksum, 8)) { | 109 | /* This field does not need special treatment (getOctal) */ |
110 | if (sum != xstrtoul(tar.chksum, 8)) { | ||
90 | bb_error_msg_and_die("invalid tar header checksum"); | 111 | bb_error_msg_and_die("invalid tar header checksum"); |
91 | } | 112 | } |
92 | 113 | ||
@@ -101,30 +122,34 @@ char get_header_tar(archive_handle_t *archive_handle) | |||
101 | } else | 122 | } else |
102 | #endif | 123 | #endif |
103 | { | 124 | { |
104 | file_header->name = xstrndup(tar.formatted.name, 100); | 125 | file_header->name = xstrndup(tar.name, sizeof(tar.name)); |
105 | if (tar.formatted.prefix[0]) { | 126 | if (tar.prefix[0]) { |
106 | char *temp = file_header->name; | 127 | char *temp = file_header->name; |
107 | file_header->name = concat_path_file(tar.formatted.prefix, temp); | 128 | file_header->name = concat_path_file(tar.prefix, temp); |
108 | free(temp); | 129 | free(temp); |
109 | } | 130 | } |
110 | } | 131 | } |
111 | 132 | ||
112 | file_header->uid = xstrtoul(tar.formatted.uid, 8); | 133 | /* getOctal trashes subsequent field, therefore we call it |
113 | file_header->gid = xstrtoul(tar.formatted.gid, 8); | 134 | * on fields in reverse order */ |
114 | file_header->size = XSTRTOUOFF(tar.formatted.size, 8); | 135 | #define GET_OCTAL(a) getOctal((a), sizeof(a)) |
115 | file_header->mtime = xstrtoul(tar.formatted.mtime, 8); | 136 | if (tar.devmajor[0]) { |
116 | file_header->link_name = tar.formatted.linkname[0] ? | 137 | unsigned minor = GET_OCTAL(tar.devminor); |
117 | xstrdup(tar.formatted.linkname) : NULL; | 138 | unsigned major = GET_OCTAL(tar.devmajor); |
118 | if (tar.formatted.devmajor[0]) { | 139 | file_header->device = makedev(major, minor); |
119 | file_header->device = makedev(xstrtoul(tar.formatted.devmajor, 8), | ||
120 | xstrtoul(tar.formatted.devminor, 8)); | ||
121 | } | 140 | } |
122 | 141 | file_header->mtime = GET_OCTAL(tar.mtime); | |
142 | file_header->size = GET_OCTAL(tar.size); | ||
143 | file_header->gid = GET_OCTAL(tar.gid); | ||
144 | file_header->uid = GET_OCTAL(tar.uid); | ||
145 | file_header->link_name = !tar.linkname[0] ? NULL : | ||
146 | xstrndup(tar.linkname, sizeof(tar.linkname)); | ||
123 | /* Set bits 0-11 of the files mode */ | 147 | /* Set bits 0-11 of the files mode */ |
124 | file_header->mode = 07777 & xstrtoul(tar.formatted.mode, 8); | 148 | file_header->mode = 07777 & GET_OCTAL(tar.mode); |
149 | #undef GET_OCTAL | ||
125 | 150 | ||
126 | /* Set bits 12-15 of the files mode */ | 151 | /* Set bits 12-15 of the files mode */ |
127 | switch (tar.formatted.typeflag) { | 152 | switch (tar.typeflag) { |
128 | /* busybox identifies hard links as being regular files with 0 size and a link name */ | 153 | /* busybox identifies hard links as being regular files with 0 size and a link name */ |
129 | case '1': | 154 | case '1': |
130 | file_header->mode |= S_IFREG; | 155 | file_header->mode |= S_IFREG; |
@@ -138,7 +163,7 @@ char get_header_tar(archive_handle_t *archive_handle) | |||
138 | file_header->mode |= S_IFDIR; | 163 | file_header->mode |= S_IFDIR; |
139 | } else | 164 | } else |
140 | #endif | 165 | #endif |
141 | file_header->mode |= S_IFREG; | 166 | file_header->mode |= S_IFREG; |
142 | break; | 167 | break; |
143 | case '2': | 168 | case '2': |
144 | file_header->mode |= S_IFLNK; | 169 | file_header->mode |= S_IFLNK; |
@@ -156,42 +181,36 @@ char get_header_tar(archive_handle_t *archive_handle) | |||
156 | file_header->mode |= S_IFIFO; | 181 | file_header->mode |= S_IFIFO; |
157 | break; | 182 | break; |
158 | #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS | 183 | #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS |
159 | case 'L': { | 184 | case 'L': |
160 | longname = xzalloc(file_header->size + 1); | 185 | longname = xzalloc(file_header->size + 1); |
161 | xread(archive_handle->src_fd, longname, file_header->size); | 186 | xread(archive_handle->src_fd, longname, file_header->size); |
162 | archive_handle->offset += file_header->size; | 187 | archive_handle->offset += file_header->size; |
163 | 188 | return get_header_tar(archive_handle); | |
164 | return get_header_tar(archive_handle); | 189 | case 'K': |
165 | } | 190 | linkname = xzalloc(file_header->size + 1); |
166 | case 'K': { | 191 | xread(archive_handle->src_fd, linkname, file_header->size); |
167 | linkname = xzalloc(file_header->size + 1); | 192 | archive_handle->offset += file_header->size; |
168 | xread(archive_handle->src_fd, linkname, file_header->size); | 193 | file_header->name = linkname; |
169 | archive_handle->offset += file_header->size; | 194 | return get_header_tar(archive_handle); |
170 | |||
171 | file_header->name = linkname; | ||
172 | return get_header_tar(archive_handle); | ||
173 | } | ||
174 | case 'D': /* GNU dump dir */ | 195 | case 'D': /* GNU dump dir */ |
175 | case 'M': /* Continuation of multi volume archive*/ | 196 | case 'M': /* Continuation of multi volume archive */ |
176 | case 'N': /* Old GNU for names > 100 characters */ | 197 | case 'N': /* Old GNU for names > 100 characters */ |
177 | case 'S': /* Sparse file */ | 198 | case 'S': /* Sparse file */ |
178 | case 'V': /* Volume header */ | 199 | case 'V': /* Volume header */ |
179 | #endif | 200 | #endif |
180 | case 'g': /* pax global header */ | 201 | case 'g': /* pax global header */ |
181 | case 'x': /* pax extended header */ | 202 | case 'x': /* pax extended header */ |
182 | bb_error_msg("ignoring extension type %c", tar.formatted.typeflag); | 203 | bb_error_msg("ignoring extension type %c", tar.typeflag); |
183 | break; | 204 | break; |
184 | default: | 205 | default: |
185 | bb_error_msg("unknown typeflag: 0x%x", tar.formatted.typeflag); | 206 | bb_error_msg("unknown typeflag: 0x%x", tar.typeflag); |
186 | } | ||
187 | { /* Strip trailing '/' in directories */ | ||
188 | /* Must be done after mode is set as '/' is used to check if its a directory */ | ||
189 | char *tmp = last_char_is(file_header->name, '/'); | ||
190 | if (tmp) { | ||
191 | *tmp = '\0'; | ||
192 | } | ||
193 | } | 207 | } |
194 | 208 | ||
209 | /* Strip trailing '/' in directories */ | ||
210 | /* Must be done after mode is set as '/' is used to check if its a directory */ | ||
211 | cp = last_char_is(file_header->name, '/'); | ||
212 | if (cp) *cp = '\0'; | ||
213 | |||
195 | if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { | 214 | if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { |
196 | archive_handle->action_header(archive_handle->file_header); | 215 | archive_handle->action_header(archive_handle->file_header); |
197 | archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; | 216 | archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; |