Added stdlib.h for malloc() in lib/decode.c
[libtar.git] / lib / block.c
blob30a8387acb9b57a767a1315f4ed46eb793350f37
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
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 /* FIXME: the return value of this function should match the return value
28 of tar_block_read(), which is a macro which references a prototype
29 that returns a ssize_t. So far, this is safe, since tar_block_read()
30 only ever reads 512 (T_BLOCKSIZE) bytes at a time, so any difference
31 in size of ssize_t and int is of negligible risk. BUT, if
32 T_BLOCKSIZE ever changes, or ever becomes a variable parameter
33 controllable by the user, all the code that calls it,
34 including this function and all code that calls it, should be
35 fixed for security reasons.
36 Thanks to Chris Palmer for the critique.
38 int
39 th_read_internal(TAR *t)
41 int i;
42 int num_zero_blocks = 0;
44 #ifdef DEBUG
45 printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname);
46 #endif
48 while ((i = tar_block_read(t, &(t->th_buf))) == T_BLOCKSIZE)
50 /* two all-zero blocks mark EOF */
51 if (t->th_buf.name[0] == '\0')
53 num_zero_blocks++;
54 if (!BIT_ISSET(t->options, TAR_IGNORE_EOT)
55 && num_zero_blocks >= 2)
56 return 0; /* EOF */
57 else
58 continue;
61 /* verify magic and version */
62 if (BIT_ISSET(t->options, TAR_CHECK_MAGIC)
63 && strncmp(t->th_buf.magic, TMAGIC, TMAGLEN - 1) != 0)
65 #ifdef DEBUG
66 puts("!!! unknown magic value in tar header");
67 #endif
68 return -2;
71 if (BIT_ISSET(t->options, TAR_CHECK_VERSION)
72 && strncmp(t->th_buf.version, TVERSION, TVERSLEN) != 0)
74 #ifdef DEBUG
75 puts("!!! unknown version value in tar header");
76 #endif
77 return -2;
80 /* check chksum */
81 if (!BIT_ISSET(t->options, TAR_IGNORE_CRC)
82 && !th_crc_ok(t))
84 #ifdef DEBUG
85 puts("!!! tar header checksum error");
86 #endif
87 return -2;
90 break;
93 #ifdef DEBUG
94 printf("<== th_read_internal(): returning %d\n", i);
95 #endif
96 return i;
100 /* wrapper function for th_read_internal() to handle GNU extensions */
102 th_read(TAR *t)
104 int i;
105 size_t sz, j, blocks;
106 char *ptr;
108 #ifdef DEBUG
109 printf("==> th_read(t=0x%lx)\n", t);
110 #endif
112 if (t->th_buf.gnu_longname != NULL)
113 free(t->th_buf.gnu_longname);
114 if (t->th_buf.gnu_longlink != NULL)
115 free(t->th_buf.gnu_longlink);
116 memset(&(t->th_buf), 0, sizeof(struct tar_header));
118 i = th_read_internal(t);
119 if (i == 0)
120 return 1;
121 else if (i != T_BLOCKSIZE)
123 if (i != -1)
124 errno = EINVAL;
125 return -1;
128 /* check for GNU long link extention */
129 if (TH_ISLONGLINK(t))
131 sz = th_get_size(t);
132 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
133 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
135 errno = E2BIG;
136 return -1;
138 #ifdef DEBUG
139 printf(" th_read(): GNU long linkname detected "
140 "(%ld bytes, %d blocks)\n", sz, blocks);
141 #endif
142 t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE);
143 if (t->th_buf.gnu_longlink == NULL)
144 return -1;
146 for (j = 0, ptr = t->th_buf.gnu_longlink; j < blocks;
147 j++, ptr += T_BLOCKSIZE)
149 #ifdef DEBUG
150 printf(" th_read(): reading long linkname "
151 "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
152 #endif
153 i = tar_block_read(t, ptr);
154 if (i != T_BLOCKSIZE)
156 if (i != -1)
157 errno = EINVAL;
158 return -1;
160 #ifdef DEBUG
161 printf(" th_read(): read block == \"%s\"\n", ptr);
162 #endif
164 #ifdef DEBUG
165 printf(" th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
166 t->th_buf.gnu_longlink);
167 #endif
169 i = th_read_internal(t);
170 if (i != T_BLOCKSIZE)
172 if (i != -1)
173 errno = EINVAL;
174 return -1;
178 /* check for GNU long name extention */
179 if (TH_ISLONGNAME(t))
181 sz = th_get_size(t);
182 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
183 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
185 errno = E2BIG;
186 return -1;
188 #ifdef DEBUG
189 printf(" th_read(): GNU long filename detected "
190 "(%ld bytes, %d blocks)\n", sz, blocks);
191 #endif
192 t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE);
193 if (t->th_buf.gnu_longname == NULL)
194 return -1;
196 for (j = 0, ptr = t->th_buf.gnu_longname; j < blocks;
197 j++, ptr += T_BLOCKSIZE)
199 #ifdef DEBUG
200 printf(" th_read(): reading long filename "
201 "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
202 #endif
203 i = tar_block_read(t, ptr);
204 if (i != T_BLOCKSIZE)
206 if (i != -1)
207 errno = EINVAL;
208 return -1;
210 #ifdef DEBUG
211 printf(" th_read(): read block == \"%s\"\n", ptr);
212 #endif
214 #ifdef DEBUG
215 printf(" th_read(): t->th_buf.gnu_longname == \"%s\"\n",
216 t->th_buf.gnu_longname);
217 #endif
219 i = th_read_internal(t);
220 if (i != T_BLOCKSIZE)
222 if (i != -1)
223 errno = EINVAL;
224 return -1;
228 #if 0
230 ** work-around for old archive files with broken typeflag fields
231 ** NOTE: I fixed this in the TH_IS*() macros instead
235 ** (directories are signified with a trailing '/')
237 if (t->th_buf.typeflag == AREGTYPE
238 && t->th_buf.name[strlen(t->th_buf.name) - 1] == '/')
239 t->th_buf.typeflag = DIRTYPE;
242 ** fallback to using mode bits
244 if (t->th_buf.typeflag == AREGTYPE)
246 mode = (mode_t)oct_to_int(t->th_buf.mode);
248 if (S_ISREG(mode))
249 t->th_buf.typeflag = REGTYPE;
250 else if (S_ISDIR(mode))
251 t->th_buf.typeflag = DIRTYPE;
252 else if (S_ISFIFO(mode))
253 t->th_buf.typeflag = FIFOTYPE;
254 else if (S_ISCHR(mode))
255 t->th_buf.typeflag = CHRTYPE;
256 else if (S_ISBLK(mode))
257 t->th_buf.typeflag = BLKTYPE;
258 else if (S_ISLNK(mode))
259 t->th_buf.typeflag = SYMTYPE;
261 #endif
263 return 0;
267 /* write a header block */
269 th_write(TAR *t)
271 int i, j;
272 char type2;
273 size_t sz, sz2;
274 char *ptr;
275 char buf[T_BLOCKSIZE];
277 #ifdef DEBUG
278 printf("==> th_write(TAR=\"%s\")\n", t->pathname);
279 th_print(t);
280 #endif
282 if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
284 #ifdef DEBUG
285 printf("th_write(): using gnu_longlink (\"%s\")\n",
286 t->th_buf.gnu_longlink);
287 #endif
288 /* save old size and type */
289 type2 = t->th_buf.typeflag;
290 sz2 = th_get_size(t);
292 /* write out initial header block with fake size and type */
293 t->th_buf.typeflag = GNU_LONGLINK_TYPE;
294 sz = strlen(t->th_buf.gnu_longlink);
295 th_set_size(t, sz);
296 th_finish(t);
297 i = tar_block_write(t, &(t->th_buf));
298 if (i != T_BLOCKSIZE)
300 if (i != -1)
301 errno = EINVAL;
302 return -1;
305 /* write out extra blocks containing long name */
306 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
307 ptr = t->th_buf.gnu_longlink; j > 1;
308 j--, ptr += T_BLOCKSIZE)
310 i = tar_block_write(t, ptr);
311 if (i != T_BLOCKSIZE)
313 if (i != -1)
314 errno = EINVAL;
315 return -1;
318 memset(buf, 0, T_BLOCKSIZE);
319 strncpy(buf, ptr, T_BLOCKSIZE);
320 i = tar_block_write(t, &buf);
321 if (i != T_BLOCKSIZE)
323 if (i != -1)
324 errno = EINVAL;
325 return -1;
328 /* reset type and size to original values */
329 t->th_buf.typeflag = type2;
330 th_set_size(t, sz2);
333 if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
335 #ifdef DEBUG
336 printf("th_write(): using gnu_longname (\"%s\")\n",
337 t->th_buf.gnu_longname);
338 #endif
339 /* save old size and type */
340 type2 = t->th_buf.typeflag;
341 sz2 = th_get_size(t);
343 /* write out initial header block with fake size and type */
344 t->th_buf.typeflag = GNU_LONGNAME_TYPE;
345 sz = strlen(t->th_buf.gnu_longname);
346 th_set_size(t, sz);
347 th_finish(t);
348 i = tar_block_write(t, &(t->th_buf));
349 if (i != T_BLOCKSIZE)
351 if (i != -1)
352 errno = EINVAL;
353 return -1;
356 /* write out extra blocks containing long name */
357 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
358 ptr = t->th_buf.gnu_longname; j > 1;
359 j--, ptr += T_BLOCKSIZE)
361 i = tar_block_write(t, ptr);
362 if (i != T_BLOCKSIZE)
364 if (i != -1)
365 errno = EINVAL;
366 return -1;
369 memset(buf, 0, T_BLOCKSIZE);
370 strncpy(buf, ptr, T_BLOCKSIZE);
371 i = tar_block_write(t, &buf);
372 if (i != T_BLOCKSIZE)
374 if (i != -1)
375 errno = EINVAL;
376 return -1;
379 /* reset type and size to original values */
380 t->th_buf.typeflag = type2;
381 th_set_size(t, sz2);
384 th_finish(t);
386 #ifdef DEBUG
387 /* print tar header */
388 th_print(t);
389 #endif
391 i = tar_block_write(t, &(t->th_buf));
392 if (i != T_BLOCKSIZE)
394 if (i != -1)
395 errno = EINVAL;
396 return -1;
399 #ifdef DEBUG
400 puts("th_write(): returning 0");
401 #endif
402 return 0;