Annotation of /trunk/mkinitrd-magellan/busybox/archival/libunarchive/get_header_tar.c
Parent Directory | Revision Log
Revision 532 -
(hide annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 8090 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 8090 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | niro | 532 | /* 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) | ||
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 int end; | ||
49 | |||
50 | file_header_t *file_header = archive_handle->file_header; | ||
51 | struct { | ||
52 | /* ustar header, Posix 1003.1 */ | ||
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 */ | ||
70 | } tar; | ||
71 | char *cp; | ||
72 | int sum, i; | ||
73 | int parse_names; | ||
74 | |||
75 | if (sizeof(tar) != 512) | ||
76 | BUG_tar_header_size(); | ||
77 | |||
78 | #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS | ||
79 | again: | ||
80 | #endif | ||
81 | /* Align header */ | ||
82 | data_align(archive_handle, 512); | ||
83 | |||
84 | again_after_align: | ||
85 | |||
86 | xread(archive_handle->src_fd, &tar, 512); | ||
87 | archive_handle->offset += 512; | ||
88 | |||
89 | /* If there is no filename its an empty header */ | ||
90 | if (tar.name[0] == 0) { | ||
91 | if (end) { | ||
92 | /* This is the second consecutive empty header! End of archive! | ||
93 | * Read until the end to empty the pipe from gz or bz2 | ||
94 | */ | ||
95 | while (full_read(archive_handle->src_fd, &tar, 512) == 512) | ||
96 | /* repeat */; | ||
97 | return EXIT_FAILURE; | ||
98 | } | ||
99 | end = 1; | ||
100 | return EXIT_SUCCESS; | ||
101 | } | ||
102 | end = 0; | ||
103 | |||
104 | /* Check header has valid magic, "ustar" is for the proper tar | ||
105 | * 0's are for the old tar format | ||
106 | */ | ||
107 | if (strncmp(tar.magic, "ustar", 5) != 0) { | ||
108 | #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY | ||
109 | if (memcmp(tar.magic, "\0\0\0\0", 5) != 0) | ||
110 | #endif | ||
111 | bb_error_msg_and_die("invalid tar magic"); | ||
112 | } | ||
113 | /* Do checksum on headers */ | ||
114 | sum = ' ' * sizeof(tar.chksum); | ||
115 | for (i = 0; i < 148 ; i++) { | ||
116 | sum += ((char*)&tar)[i]; | ||
117 | } | ||
118 | for (i = 156; i < 512 ; i++) { | ||
119 | sum += ((char*)&tar)[i]; | ||
120 | } | ||
121 | /* This field does not need special treatment (getOctal) */ | ||
122 | if (sum != xstrtoul(tar.chksum, 8)) { | ||
123 | bb_error_msg_and_die("invalid tar header checksum"); | ||
124 | } | ||
125 | |||
126 | /* 0 is reserved for high perf file, treat as normal file */ | ||
127 | if (!tar.typeflag) tar.typeflag = '0'; | ||
128 | parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7'); | ||
129 | |||
130 | /* getOctal trashes subsequent field, therefore we call it | ||
131 | * on fields in reverse order */ | ||
132 | if (tar.devmajor[0]) { | ||
133 | unsigned minor = GET_OCTAL(tar.devminor); | ||
134 | unsigned major = GET_OCTAL(tar.devmajor); | ||
135 | file_header->device = makedev(major, minor); | ||
136 | } | ||
137 | file_header->link_name = NULL; | ||
138 | if (!linkname && parse_names && tar.linkname[0]) { | ||
139 | /* we trash magic[0] here, it's ok */ | ||
140 | tar.linkname[sizeof(tar.linkname)] = '\0'; | ||
141 | file_header->link_name = xstrdup(tar.linkname); | ||
142 | /* FIXME: what if we have non-link object with link_name? */ | ||
143 | /* Will link_name be free()ed? */ | ||
144 | } | ||
145 | file_header->mtime = GET_OCTAL(tar.mtime); | ||
146 | file_header->size = GET_OCTAL(tar.size); | ||
147 | file_header->gid = GET_OCTAL(tar.gid); | ||
148 | file_header->uid = GET_OCTAL(tar.uid); | ||
149 | /* Set bits 0-11 of the files mode */ | ||
150 | file_header->mode = 07777 & GET_OCTAL(tar.mode); | ||
151 | |||
152 | file_header->name = NULL; | ||
153 | if (!longname && parse_names) { | ||
154 | /* we trash mode[0] here, it's ok */ | ||
155 | tar.name[sizeof(tar.name)] = '\0'; | ||
156 | if (tar.prefix[0]) { | ||
157 | /* and padding[0] */ | ||
158 | tar.prefix[sizeof(tar.prefix)] = '\0'; | ||
159 | file_header->name = concat_path_file(tar.prefix, tar.name); | ||
160 | } else | ||
161 | file_header->name = xstrdup(tar.name); | ||
162 | } | ||
163 | |||
164 | /* Set bits 12-15 of the files mode */ | ||
165 | /* (typeflag was not trashed because chksum does not use getOctal) */ | ||
166 | switch (tar.typeflag) { | ||
167 | /* busybox identifies hard links as being regular files with 0 size and a link name */ | ||
168 | case '1': | ||
169 | file_header->mode |= S_IFREG; | ||
170 | break; | ||
171 | case '7': | ||
172 | /* case 0: */ | ||
173 | case '0': | ||
174 | #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY | ||
175 | if (last_char_is(file_header->name, '/')) { | ||
176 | file_header->mode |= S_IFDIR; | ||
177 | } else | ||
178 | #endif | ||
179 | file_header->mode |= S_IFREG; | ||
180 | break; | ||
181 | case '2': | ||
182 | file_header->mode |= S_IFLNK; | ||
183 | break; | ||
184 | case '3': | ||
185 | file_header->mode |= S_IFCHR; | ||
186 | break; | ||
187 | case '4': | ||
188 | file_header->mode |= S_IFBLK; | ||
189 | break; | ||
190 | case '5': | ||
191 | file_header->mode |= S_IFDIR; | ||
192 | break; | ||
193 | case '6': | ||
194 | file_header->mode |= S_IFIFO; | ||
195 | break; | ||
196 | #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS | ||
197 | case 'L': | ||
198 | /* free: paranoia: tar with several consecutive longnames */ | ||
199 | free(longname); | ||
200 | /* For paranoia reasons we allocate extra NUL char */ | ||
201 | longname = xzalloc(file_header->size + 1); | ||
202 | /* We read ASCIZ string, including NUL */ | ||
203 | xread(archive_handle->src_fd, longname, file_header->size); | ||
204 | archive_handle->offset += file_header->size; | ||
205 | /* return get_header_tar(archive_handle); */ | ||
206 | /* gcc 4.1.1 didn't optimize it into jump */ | ||
207 | /* so we will do it ourself, this also saves stack */ | ||
208 | goto again; | ||
209 | case 'K': | ||
210 | free(linkname); | ||
211 | linkname = xzalloc(file_header->size + 1); | ||
212 | xread(archive_handle->src_fd, linkname, file_header->size); | ||
213 | archive_handle->offset += file_header->size; | ||
214 | /* return get_header_tar(archive_handle); */ | ||
215 | goto again; | ||
216 | case 'D': /* GNU dump dir */ | ||
217 | case 'M': /* Continuation of multi volume archive */ | ||
218 | case 'N': /* Old GNU for names > 100 characters */ | ||
219 | case 'S': /* Sparse file */ | ||
220 | case 'V': /* Volume header */ | ||
221 | #endif | ||
222 | case 'g': /* pax global header */ | ||
223 | case 'x': { /* pax extended header */ | ||
224 | off_t sz; | ||
225 | bb_error_msg("warning: skipping header '%c'", tar.typeflag); | ||
226 | sz = (file_header->size + 511) & ~(off_t)511; | ||
227 | archive_handle->offset += sz; | ||
228 | sz >>= 9; /* sz /= 512 but w/o contortions for signed div */ | ||
229 | while (sz--) | ||
230 | xread(archive_handle->src_fd, &tar, 512); | ||
231 | /* return get_header_tar(archive_handle); */ | ||
232 | goto again_after_align; | ||
233 | } | ||
234 | default: | ||
235 | bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag); | ||
236 | } | ||
237 | |||
238 | #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS | ||
239 | if (longname) { | ||
240 | file_header->name = longname; | ||
241 | longname = NULL; | ||
242 | } | ||
243 | if (linkname) { | ||
244 | file_header->link_name = linkname; | ||
245 | linkname = NULL; | ||
246 | } | ||
247 | #endif | ||
248 | if (!strncmp(file_header->name, "/../"+1, 3) | ||
249 | || strstr(file_header->name, "/../") | ||
250 | ) { | ||
251 | bb_error_msg_and_die("name with '..' encountered: '%s'", | ||
252 | file_header->name); | ||
253 | } | ||
254 | |||
255 | /* Strip trailing '/' in directories */ | ||
256 | /* Must be done after mode is set as '/' is used to check if it's a directory */ | ||
257 | cp = last_char_is(file_header->name, '/'); | ||
258 | |||
259 | if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { | ||
260 | archive_handle->action_header(archive_handle->file_header); | ||
261 | /* Note that we kill the '/' only after action_header() */ | ||
262 | /* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */ | ||
263 | if (cp) *cp = '\0'; | ||
264 | archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; | ||
265 | archive_handle->action_data(archive_handle); | ||
266 | llist_add_to(&(archive_handle->passed), file_header->name); | ||
267 | } else { | ||
268 | data_skip(archive_handle); | ||
269 | free(file_header->name); | ||
270 | } | ||
271 | archive_handle->offset += file_header->size; | ||
272 | |||
273 | free(file_header->link_name); | ||
274 | /* Do not free(file_header->name)! */ | ||
275 | |||
276 | return EXIT_SUCCESS; | ||
277 | } |