Fixed size_t overflow bug, as reported by Timo Warns
[libtar.git] / lib / block.c
blob092bc282edfb4c9262c62802318d111b40894593
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 */
13 #include <internal.h>
15 #include <errno.h>
17 #ifdef STDC_HEADERS
18 # include <string.h>
19 # include <stdlib.h>
20 #endif
23 #define BIT_ISSET(bitmask, bit) ((bitmask) & (bit))
26 /* read a header block */
27 int
28 th_read_internal(TAR *t)
29 {
30 int i;
31 int num_zero_blocks = 0;
33 #ifdef DEBUG
34 printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname);
35 #endif
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 }
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 }
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 }
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 }
79 break;
80 }
82 #ifdef DEBUG
83 printf("<== th_read_internal(): returning %d\n", i);
84 #endif
85 return i;
86 }
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;
97 #ifdef DEBUG
98 printf("==> th_read(t=0x%lx)\n", t);
99 #endif
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));
107 i = th_read_internal(t);
108 if (i == 0)
109 return 1;
110 else if (i != T_BLOCKSIZE)
112 if (i != -1)
113 errno = EINVAL;
114 return -1;
117 /* check for GNU long link extention */
118 if (TH_ISLONGLINK(t))
120 sz = th_get_size(t);
121 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
122 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
124 errno = E2BIG;
125 return -1;
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;
135 for (j = 0, ptr = t->th_buf.gnu_longlink; j < blocks;
136 j++, ptr += T_BLOCKSIZE)
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)
145 if (i != -1)
146 errno = EINVAL;
147 return -1;
149 #ifdef DEBUG
150 printf(" th_read(): read block == \"%s\"\n", ptr);
151 #endif
153 #ifdef DEBUG
154 printf(" th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
155 t->th_buf.gnu_longlink);
156 #endif
158 i = th_read_internal(t);
159 if (i != T_BLOCKSIZE)
161 if (i != -1)
162 errno = EINVAL;
163 return -1;
167 /* check for GNU long name extention */
168 if (TH_ISLONGNAME(t))
170 sz = th_get_size(t);
171 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
172 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
174 errno = E2BIG;
175 return -1;
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;
185 for (j = 0, ptr = t->th_buf.gnu_longname; j < blocks;
186 j++, ptr += T_BLOCKSIZE)
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)
195 if (i != -1)
196 errno = EINVAL;
197 return -1;
199 #ifdef DEBUG
200 printf(" th_read(): read block == \"%s\"\n", ptr);
201 #endif
203 #ifdef DEBUG
204 printf(" th_read(): t->th_buf.gnu_longname == \"%s\"\n",
205 t->th_buf.gnu_longname);
206 #endif
208 i = th_read_internal(t);
209 if (i != T_BLOCKSIZE)
211 if (i != -1)
212 errno = EINVAL;
213 return -1;
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 */
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;
230 /*
231 ** fallback to using mode bits
232 */
233 if (t->th_buf.typeflag == AREGTYPE)
235 mode = (mode_t)oct_to_int(t->th_buf.mode);
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;
250 #endif
252 return 0;
256 /* write a header block */
257 int
258 th_write(TAR *t)
260 int i, j;
261 char type2;
262 size_t sz, sz2;
263 char *ptr;
264 char buf[T_BLOCKSIZE];
266 #ifdef DEBUG
267 printf("==> th_write(TAR=\"%s\")\n", t->pathname);
268 th_print(t);
269 #endif
271 if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
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);
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)
289 if (i != -1)
290 errno = EINVAL;
291 return -1;
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)
299 i = tar_block_write(t, ptr);
300 if (i != T_BLOCKSIZE)
302 if (i != -1)
303 errno = EINVAL;
304 return -1;
307 memset(buf, 0, T_BLOCKSIZE);
308 strncpy(buf, ptr, T_BLOCKSIZE);
309 i = tar_block_write(t, &buf);
310 if (i != T_BLOCKSIZE)
312 if (i != -1)
313 errno = EINVAL;
314 return -1;
317 /* reset type and size to original values */
318 t->th_buf.typeflag = type2;
319 th_set_size(t, sz2);
322 if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
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);
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)
340 if (i != -1)
341 errno = EINVAL;
342 return -1;
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)
350 i = tar_block_write(t, ptr);
351 if (i != T_BLOCKSIZE)
353 if (i != -1)
354 errno = EINVAL;
355 return -1;
358 memset(buf, 0, T_BLOCKSIZE);
359 strncpy(buf, ptr, T_BLOCKSIZE);
360 i = tar_block_write(t, &buf);
361 if (i != T_BLOCKSIZE)
363 if (i != -1)
364 errno = EINVAL;
365 return -1;
368 /* reset type and size to original values */
369 t->th_buf.typeflag = type2;
370 th_set_size(t, sz2);
373 th_finish(t);
375 #ifdef DEBUG
376 /* print tar header */
377 th_print(t);
378 #endif
380 i = tar_block_write(t, &(t->th_buf));
381 if (i != T_BLOCKSIZE)
383 if (i != -1)
384 errno = EINVAL;
385 return -1;
388 #ifdef DEBUG
389 puts("th_write(): returning 0");
390 #endif
391 return 0;