Fixed size_t overflow bug, as reported by Timo Warns
[libtar.git] / lib / block.c
1 /*
2 ** Copyright 1998-2003 University of Illinois Board of Trustees
3 ** Copyright 1998-2003 Mark D. Roth
4 ** All rights reserved.
5 **
6 ** block.c - libtar code to handle tar archive header blocks
7 **
8 ** Mark D. Roth <roth@uiuc.edu>
9 ** Campus Information Technologies and Educational Services
10 ** University of Illinois at Urbana-Champaign
11 */
12
13 #include <internal.h>
14
15 #include <errno.h>
16
17 #ifdef STDC_HEADERS
18 # include <string.h>
19 # include <stdlib.h>
20 #endif
21
22
23 #define BIT_ISSET(bitmask, bit) ((bitmask) & (bit))
24
25
26 /* read a header block */
27 int
28 th_read_internal(TAR *t)
29 {
30 int i;
31 int num_zero_blocks = 0;
32
33 #ifdef DEBUG
34 printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname);
35 #endif
36
37 while ((i = tar_block_read(t, &(t->th_buf))) == T_BLOCKSIZE)
38 {
39 /* two all-zero blocks mark EOF */
40 if (t->th_buf.name[0] == '\0')
41 {
42 num_zero_blocks++;
43 if (!BIT_ISSET(t->options, TAR_IGNORE_EOT)
44 && num_zero_blocks >= 2)
45 return 0; /* EOF */
46 else
47 continue;
48 }
49
50 /* verify magic and version */
51 if (BIT_ISSET(t->options, TAR_CHECK_MAGIC)
52 && strncmp(t->th_buf.magic, TMAGIC, TMAGLEN - 1) != 0)
53 {
54 #ifdef DEBUG
55 puts("!!! unknown magic value in tar header");
56 #endif
57 return -2;
58 }
59
60 if (BIT_ISSET(t->options, TAR_CHECK_VERSION)
61 && strncmp(t->th_buf.version, TVERSION, TVERSLEN) != 0)
62 {
63 #ifdef DEBUG
64 puts("!!! unknown version value in tar header");
65 #endif
66 return -2;
67 }
68
69 /* check chksum */
70 if (!BIT_ISSET(t->options, TAR_IGNORE_CRC)
71 && !th_crc_ok(t))
72 {
73 #ifdef DEBUG
74 puts("!!! tar header checksum error");
75 #endif
76 return -2;
77 }
78
79 break;
80 }
81
82 #ifdef DEBUG
83 printf("<== th_read_internal(): returning %d\n", i);
84 #endif
85 return i;
86 }
87
88
89 /* wrapper function for th_read_internal() to handle GNU extensions */
90 int
91 th_read(TAR *t)
92 {
93 int i;
94 size_t sz, j, blocks;
95 char *ptr;
96
97 #ifdef DEBUG
98 printf("==> th_read(t=0x%lx)\n", t);
99 #endif
100
101 if (t->th_buf.gnu_longname != NULL)
102 free(t->th_buf.gnu_longname);
103 if (t->th_buf.gnu_longlink != NULL)
104 free(t->th_buf.gnu_longlink);
105 memset(&(t->th_buf), 0, sizeof(struct tar_header));
106
107 i = th_read_internal(t);
108 if (i == 0)
109 return 1;
110 else if (i != T_BLOCKSIZE)
111 {
112 if (i != -1)
113 errno = EINVAL;
114 return -1;
115 }
116
117 /* check for GNU long link extention */
118 if (TH_ISLONGLINK(t))
119 {
120 sz = th_get_size(t);
121 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
122 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
123 {
124 errno = E2BIG;
125 return -1;
126 }
127 #ifdef DEBUG
128 printf(" th_read(): GNU long linkname detected "
129 "(%ld bytes, %d blocks)\n", sz, blocks);
130 #endif
131 t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE);
132 if (t->th_buf.gnu_longlink == NULL)
133 return -1;
134
135 for (j = 0, ptr = t->th_buf.gnu_longlink; j < blocks;
136 j++, ptr += T_BLOCKSIZE)
137 {
138 #ifdef DEBUG
139 printf(" th_read(): reading long linkname "
140 "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
141 #endif
142 i = tar_block_read(t, ptr);
143 if (i != T_BLOCKSIZE)
144 {
145 if (i != -1)
146 errno = EINVAL;
147 return -1;
148 }
149 #ifdef DEBUG
150 printf(" th_read(): read block == \"%s\"\n", ptr);
151 #endif
152 }
153 #ifdef DEBUG
154 printf(" th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
155 t->th_buf.gnu_longlink);
156 #endif
157
158 i = th_read_internal(t);
159 if (i != T_BLOCKSIZE)
160 {
161 if (i != -1)
162 errno = EINVAL;
163 return -1;
164 }
165 }
166
167 /* check for GNU long name extention */
168 if (TH_ISLONGNAME(t))
169 {
170 sz = th_get_size(t);
171 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
172 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
173 {
174 errno = E2BIG;
175 return -1;
176 }
177 #ifdef DEBUG
178 printf(" th_read(): GNU long filename detected "
179 "(%ld bytes, %d blocks)\n", sz, blocks);
180 #endif
181 t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE);
182 if (t->th_buf.gnu_longname == NULL)
183 return -1;
184
185 for (j = 0, ptr = t->th_buf.gnu_longname; j < blocks;
186 j++, ptr += T_BLOCKSIZE)
187 {
188 #ifdef DEBUG
189 printf(" th_read(): reading long filename "
190 "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
191 #endif
192 i = tar_block_read(t, ptr);
193 if (i != T_BLOCKSIZE)
194 {
195 if (i != -1)
196 errno = EINVAL;
197 return -1;
198 }
199 #ifdef DEBUG
200 printf(" th_read(): read block == \"%s\"\n", ptr);
201 #endif
202 }
203 #ifdef DEBUG
204 printf(" th_read(): t->th_buf.gnu_longname == \"%s\"\n",
205 t->th_buf.gnu_longname);
206 #endif
207
208 i = th_read_internal(t);
209 if (i != T_BLOCKSIZE)
210 {
211 if (i != -1)
212 errno = EINVAL;
213 return -1;
214 }
215 }
216
217 #if 0
218 /*
219 ** work-around for old archive files with broken typeflag fields
220 ** NOTE: I fixed this in the TH_IS*() macros instead
221 */
222
223 /*
224 ** (directories are signified with a trailing '/')
225 */
226 if (t->th_buf.typeflag == AREGTYPE
227 && t->th_buf.name[strlen(t->th_buf.name) - 1] == '/')
228 t->th_buf.typeflag = DIRTYPE;
229
230 /*
231 ** fallback to using mode bits
232 */
233 if (t->th_buf.typeflag == AREGTYPE)
234 {
235 mode = (mode_t)oct_to_int(t->th_buf.mode);
236
237 if (S_ISREG(mode))
238 t->th_buf.typeflag = REGTYPE;
239 else if (S_ISDIR(mode))
240 t->th_buf.typeflag = DIRTYPE;
241 else if (S_ISFIFO(mode))
242 t->th_buf.typeflag = FIFOTYPE;
243 else if (S_ISCHR(mode))
244 t->th_buf.typeflag = CHRTYPE;
245 else if (S_ISBLK(mode))
246 t->th_buf.typeflag = BLKTYPE;
247 else if (S_ISLNK(mode))
248 t->th_buf.typeflag = SYMTYPE;
249 }
250 #endif
251
252 return 0;
253 }
254
255
256 /* write a header block */
257 int
258 th_write(TAR *t)
259 {
260 int i, j;
261 char type2;
262 size_t sz, sz2;
263 char *ptr;
264 char buf[T_BLOCKSIZE];
265
266 #ifdef DEBUG
267 printf("==> th_write(TAR=\"%s\")\n", t->pathname);
268 th_print(t);
269 #endif
270
271 if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
272 {
273 #ifdef DEBUG
274 printf("th_write(): using gnu_longlink (\"%s\")\n",
275 t->th_buf.gnu_longlink);
276 #endif
277 /* save old size and type */
278 type2 = t->th_buf.typeflag;
279 sz2 = th_get_size(t);
280
281 /* write out initial header block with fake size and type */
282 t->th_buf.typeflag = GNU_LONGLINK_TYPE;
283 sz = strlen(t->th_buf.gnu_longlink);
284 th_set_size(t, sz);
285 th_finish(t);
286 i = tar_block_write(t, &(t->th_buf));
287 if (i != T_BLOCKSIZE)
288 {
289 if (i != -1)
290 errno = EINVAL;
291 return -1;
292 }
293
294 /* write out extra blocks containing long name */
295 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
296 ptr = t->th_buf.gnu_longlink; j > 1;
297 j--, ptr += T_BLOCKSIZE)
298 {
299 i = tar_block_write(t, ptr);
300 if (i != T_BLOCKSIZE)
301 {
302 if (i != -1)
303 errno = EINVAL;
304 return -1;
305 }
306 }
307 memset(buf, 0, T_BLOCKSIZE);
308 strncpy(buf, ptr, T_BLOCKSIZE);
309 i = tar_block_write(t, &buf);
310 if (i != T_BLOCKSIZE)
311 {
312 if (i != -1)
313 errno = EINVAL;
314 return -1;
315 }
316
317 /* reset type and size to original values */
318 t->th_buf.typeflag = type2;
319 th_set_size(t, sz2);
320 }
321
322 if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
323 {
324 #ifdef DEBUG
325 printf("th_write(): using gnu_longname (\"%s\")\n",
326 t->th_buf.gnu_longname);
327 #endif
328 /* save old size and type */
329 type2 = t->th_buf.typeflag;
330 sz2 = th_get_size(t);
331
332 /* write out initial header block with fake size and type */
333 t->th_buf.typeflag = GNU_LONGNAME_TYPE;
334 sz = strlen(t->th_buf.gnu_longname);
335 th_set_size(t, sz);
336 th_finish(t);
337 i = tar_block_write(t, &(t->th_buf));
338 if (i != T_BLOCKSIZE)
339 {
340 if (i != -1)
341 errno = EINVAL;
342 return -1;
343 }
344
345 /* write out extra blocks containing long name */
346 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
347 ptr = t->th_buf.gnu_longname; j > 1;
348 j--, ptr += T_BLOCKSIZE)
349 {
350 i = tar_block_write(t, ptr);
351 if (i != T_BLOCKSIZE)
352 {
353 if (i != -1)
354 errno = EINVAL;
355 return -1;
356 }
357 }
358 memset(buf, 0, T_BLOCKSIZE);
359 strncpy(buf, ptr, T_BLOCKSIZE);
360 i = tar_block_write(t, &buf);
361 if (i != T_BLOCKSIZE)
362 {
363 if (i != -1)
364 errno = EINVAL;
365 return -1;
366 }
367
368 /* reset type and size to original values */
369 t->th_buf.typeflag = type2;
370 th_set_size(t, sz2);
371 }
372
373 th_finish(t);
374
375 #ifdef DEBUG
376 /* print tar header */
377 th_print(t);
378 #endif
379
380 i = tar_block_write(t, &(t->th_buf));
381 if (i != T_BLOCKSIZE)
382 {
383 if (i != -1)
384 errno = EINVAL;
385 return -1;
386 }
387
388 #ifdef DEBUG
389 puts("th_write(): returning 0");
390 #endif
391 return 0;
392 }
393
394