blob: 6ed9e6000be03dd8f5028070a4eeea65b89a445f [file] [log] [blame]
bigbiff bigbiff9c754052013-01-09 09:09:08 -05001/*
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>
Matt Mower2b18a532015-02-20 16:58:05 -060014#include <stdio.h>
bigbiff bigbiff9c754052013-01-09 09:09:08 -050015#include <errno.h>
16
17#ifdef STDC_HEADERS
18# include <string.h>
19# include <stdlib.h>
20#endif
21
22#define BIT_ISSET(bitmask, bit) ((bitmask) & (bit))
23
Vojtech Bocek25fd68d2013-08-27 03:10:10 +020024// Used to identify selinux_context in extended ('x')
25// metadata. From RedHat implementation.
26#define SELINUX_TAG "RHT.security.selinux="
27#define SELINUX_TAG_LEN 21
bigbiff bigbiff9c754052013-01-09 09:09:08 -050028
29/* read a header block */
30int
31th_read_internal(TAR *t)
32{
33 int i;
34 int num_zero_blocks = 0;
35
36#ifdef DEBUG
37 printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname);
38#endif
39
40 while ((i = tar_block_read(t, &(t->th_buf))) == T_BLOCKSIZE)
41 {
42 /* two all-zero blocks mark EOF */
43 if (t->th_buf.name[0] == '\0')
44 {
45 num_zero_blocks++;
46 if (!BIT_ISSET(t->options, TAR_IGNORE_EOT)
47 && num_zero_blocks >= 2)
48 return 0; /* EOF */
49 else
50 continue;
51 }
52
53 /* verify magic and version */
54 if (BIT_ISSET(t->options, TAR_CHECK_MAGIC)
55 && strncmp(t->th_buf.magic, TMAGIC, TMAGLEN - 1) != 0)
56 {
57#ifdef DEBUG
58 puts("!!! unknown magic value in tar header");
59#endif
60 return -2;
61 }
62
63 if (BIT_ISSET(t->options, TAR_CHECK_VERSION)
64 && strncmp(t->th_buf.version, TVERSION, TVERSLEN) != 0)
65 {
66#ifdef DEBUG
67 puts("!!! unknown version value in tar header");
68#endif
69 return -2;
70 }
71
72 /* check chksum */
73 if (!BIT_ISSET(t->options, TAR_IGNORE_CRC)
74 && !th_crc_ok(t))
75 {
76#ifdef DEBUG
77 puts("!!! tar header checksum error");
78#endif
79 return -2;
80 }
81
82 break;
83 }
84
85#ifdef DEBUG
86 printf("<== th_read_internal(): returning %d\n", i);
87#endif
88 return i;
89}
90
91
92/* wrapper function for th_read_internal() to handle GNU extensions */
93int
94th_read(TAR *t)
95{
96 int i, j;
97 size_t sz;
98 char *ptr;
99
100#ifdef DEBUG
101 printf("==> th_read(t=0x%lx)\n", t);
102#endif
103
104 if (t->th_buf.gnu_longname != NULL)
105 free(t->th_buf.gnu_longname);
106 if (t->th_buf.gnu_longlink != NULL)
107 free(t->th_buf.gnu_longlink);
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200108#ifdef HAVE_SELINUX
109 if (t->th_buf.selinux_context != NULL)
110 free(t->th_buf.selinux_context);
111#endif
112
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500113 memset(&(t->th_buf), 0, sizeof(struct tar_header));
114
115 i = th_read_internal(t);
116 if (i == 0)
117 return 1;
118 else if (i != T_BLOCKSIZE)
119 {
120 if (i != -1)
121 errno = EINVAL;
122 return -1;
123 }
124
125 /* check for GNU long link extention */
126 if (TH_ISLONGLINK(t))
127 {
128 sz = th_get_size(t);
129 j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
130#ifdef DEBUG
131 printf(" th_read(): GNU long linkname detected "
132 "(%ld bytes, %d blocks)\n", sz, j);
133#endif
134 t->th_buf.gnu_longlink = (char *)malloc(j * T_BLOCKSIZE);
135 if (t->th_buf.gnu_longlink == NULL)
136 return -1;
137
138 for (ptr = t->th_buf.gnu_longlink; j > 0;
139 j--, ptr += T_BLOCKSIZE)
140 {
141#ifdef DEBUG
142 printf(" th_read(): reading long linkname "
143 "(%d blocks left, ptr == %ld)\n", j, ptr);
144#endif
145 i = tar_block_read(t, ptr);
146 if (i != T_BLOCKSIZE)
147 {
148 if (i != -1)
149 errno = EINVAL;
150 return -1;
151 }
152#ifdef DEBUG
153 printf(" th_read(): read block == \"%s\"\n", ptr);
154#endif
155 }
156#ifdef DEBUG
157 printf(" th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
158 t->th_buf.gnu_longlink);
159#endif
160
161 i = th_read_internal(t);
162 if (i != T_BLOCKSIZE)
163 {
164 if (i != -1)
165 errno = EINVAL;
166 return -1;
167 }
168 }
169
170 /* check for GNU long name extention */
171 if (TH_ISLONGNAME(t))
172 {
173 sz = th_get_size(t);
174 j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
175#ifdef DEBUG
176 printf(" th_read(): GNU long filename detected "
177 "(%ld bytes, %d blocks)\n", sz, j);
178#endif
179 t->th_buf.gnu_longname = (char *)malloc(j * T_BLOCKSIZE);
180 if (t->th_buf.gnu_longname == NULL)
181 return -1;
182
183 for (ptr = t->th_buf.gnu_longname; j > 0;
184 j--, ptr += T_BLOCKSIZE)
185 {
186#ifdef DEBUG
187 printf(" th_read(): reading long filename "
188 "(%d blocks left, ptr == %ld)\n", j, ptr);
189#endif
190 i = tar_block_read(t, ptr);
191 if (i != T_BLOCKSIZE)
192 {
193 if (i != -1)
194 errno = EINVAL;
195 return -1;
196 }
197#ifdef DEBUG
198 printf(" th_read(): read block == \"%s\"\n", ptr);
199#endif
200 }
201#ifdef DEBUG
202 printf(" th_read(): t->th_buf.gnu_longname == \"%s\"\n",
203 t->th_buf.gnu_longname);
204#endif
205
206 i = th_read_internal(t);
207 if (i != T_BLOCKSIZE)
208 {
209 if (i != -1)
210 errno = EINVAL;
211 return -1;
212 }
213 }
214
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200215#ifdef HAVE_SELINUX
216 if(TH_ISEXTHEADER(t))
217 {
218 sz = th_get_size(t);
219
220 if(sz >= T_BLOCKSIZE) // Not supported
221 {
222#ifdef DEBUG
223 printf(" th_read(): Extended header is too long!\n");
224#endif
225 }
226 else
227 {
228 char buf[T_BLOCKSIZE];
229 i = tar_block_read(t, buf);
230 if (i != T_BLOCKSIZE)
231 {
232 if (i != -1)
233 errno = EINVAL;
234 return -1;
235 }
236
237 // To be sure
238 buf[T_BLOCKSIZE-1] = 0;
239
240 int len = strlen(buf);
241 char *start = strstr(buf, SELINUX_TAG);
242 if(start && start+SELINUX_TAG_LEN < buf+len)
243 {
244 start += SELINUX_TAG_LEN;
245 char *end = strchr(start, '\n');
246 if(end)
247 {
248 t->th_buf.selinux_context = strndup(start, end-start);
249#ifdef DEBUG
250 printf(" th_read(): SELinux context xattr detected: %s\n", t->th_buf.selinux_context);
251#endif
252 }
253 }
254 }
255
256 i = th_read_internal(t);
257 if (i != T_BLOCKSIZE)
258 {
259 if (i != -1)
260 errno = EINVAL;
261 return -1;
262 }
263 }
264#endif
265
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500266#if 0
267 /*
268 ** work-around for old archive files with broken typeflag fields
269 ** NOTE: I fixed this in the TH_IS*() macros instead
270 */
271
272 /*
273 ** (directories are signified with a trailing '/')
274 */
275 if (t->th_buf.typeflag == AREGTYPE
276 && t->th_buf.name[strlen(t->th_buf.name) - 1] == '/')
277 t->th_buf.typeflag = DIRTYPE;
278
279 /*
280 ** fallback to using mode bits
281 */
282 if (t->th_buf.typeflag == AREGTYPE)
283 {
284 mode = (mode_t)oct_to_int(t->th_buf.mode);
285
286 if (S_ISREG(mode))
287 t->th_buf.typeflag = REGTYPE;
288 else if (S_ISDIR(mode))
289 t->th_buf.typeflag = DIRTYPE;
290 else if (S_ISFIFO(mode))
291 t->th_buf.typeflag = FIFOTYPE;
292 else if (S_ISCHR(mode))
293 t->th_buf.typeflag = CHRTYPE;
294 else if (S_ISBLK(mode))
295 t->th_buf.typeflag = BLKTYPE;
296 else if (S_ISLNK(mode))
297 t->th_buf.typeflag = SYMTYPE;
298 }
299#endif
300
301 return 0;
302}
303
304
305/* write a header block */
306int
307th_write(TAR *t)
308{
309 int i, j;
310 char type2;
311 size_t sz, sz2;
312 char *ptr;
313 char buf[T_BLOCKSIZE];
314
315#ifdef DEBUG
316 printf("==> th_write(TAR=\"%s\")\n", t->pathname);
317 th_print(t);
318#endif
319
320 if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
321 {
322#ifdef DEBUG
323 printf("th_write(): using gnu_longlink (\"%s\")\n",
324 t->th_buf.gnu_longlink);
325#endif
326 /* save old size and type */
327 type2 = t->th_buf.typeflag;
328 sz2 = th_get_size(t);
329
330 /* write out initial header block with fake size and type */
331 t->th_buf.typeflag = GNU_LONGLINK_TYPE;
332 sz = strlen(t->th_buf.gnu_longlink);
333 th_set_size(t, sz);
334 th_finish(t);
335 i = tar_block_write(t, &(t->th_buf));
336 if (i != T_BLOCKSIZE)
337 {
338 if (i != -1)
339 errno = EINVAL;
340 return -1;
341 }
342
343 /* write out extra blocks containing long name */
344 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
345 ptr = t->th_buf.gnu_longlink; j > 1;
346 j--, ptr += T_BLOCKSIZE)
347 {
348 i = tar_block_write(t, ptr);
349 if (i != T_BLOCKSIZE)
350 {
351 if (i != -1)
352 errno = EINVAL;
353 return -1;
354 }
355 }
356 memset(buf, 0, T_BLOCKSIZE);
357 strncpy(buf, ptr, T_BLOCKSIZE);
358 i = tar_block_write(t, &buf);
359 if (i != T_BLOCKSIZE)
360 {
361 if (i != -1)
362 errno = EINVAL;
363 return -1;
364 }
365
366 /* reset type and size to original values */
367 t->th_buf.typeflag = type2;
368 th_set_size(t, sz2);
369 }
370
371 if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
372 {
373#ifdef DEBUG
374 printf("th_write(): using gnu_longname (\"%s\")\n",
375 t->th_buf.gnu_longname);
376#endif
377 /* save old size and type */
378 type2 = t->th_buf.typeflag;
379 sz2 = th_get_size(t);
380
381 /* write out initial header block with fake size and type */
382 t->th_buf.typeflag = GNU_LONGNAME_TYPE;
383 sz = strlen(t->th_buf.gnu_longname);
384 th_set_size(t, sz);
385 th_finish(t);
386 i = tar_block_write(t, &(t->th_buf));
387 if (i != T_BLOCKSIZE)
388 {
389 if (i != -1)
390 errno = EINVAL;
391 return -1;
392 }
393
394 /* write out extra blocks containing long name */
395 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
396 ptr = t->th_buf.gnu_longname; j > 1;
397 j--, ptr += T_BLOCKSIZE)
398 {
399 i = tar_block_write(t, ptr);
400 if (i != T_BLOCKSIZE)
401 {
402 if (i != -1)
403 errno = EINVAL;
404 return -1;
405 }
406 }
407 memset(buf, 0, T_BLOCKSIZE);
408 strncpy(buf, ptr, T_BLOCKSIZE);
409 i = tar_block_write(t, &buf);
410 if (i != T_BLOCKSIZE)
411 {
412 if (i != -1)
413 errno = EINVAL;
414 return -1;
415 }
416
417 /* reset type and size to original values */
418 t->th_buf.typeflag = type2;
419 th_set_size(t, sz2);
420 }
421
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200422#ifdef HAVE_SELINUX
423 if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
424 {
425#ifdef DEBUG
426 printf("th_write(): using selinux_context (\"%s\")\n",
427 t->th_buf.selinux_context);
428#endif
429 /* save old size and type */
430 type2 = t->th_buf.typeflag;
431 sz2 = th_get_size(t);
432
433 /* write out initial header block with fake size and type */
434 t->th_buf.typeflag = TH_EXT_TYPE;
435
436 /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */
437 // size newline
438 sz = SELINUX_TAG_LEN + strlen(t->th_buf.selinux_context) + 3 + 1;
439
440 if(sz >= 100) // another ascci digit for size
441 ++sz;
442
443 if(sz >= T_BLOCKSIZE) // impossible
444 {
445 errno = EINVAL;
446 return -1;
447 }
448
449 th_set_size(t, sz);
450 th_finish(t);
451 i = tar_block_write(t, &(t->th_buf));
452 if (i != T_BLOCKSIZE)
453 {
454 if (i != -1)
455 errno = EINVAL;
456 return -1;
457 }
458
459 memset(buf, 0, T_BLOCKSIZE);
460 snprintf(buf, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", sz, t->th_buf.selinux_context);
461 i = tar_block_write(t, &buf);
462 if (i != T_BLOCKSIZE)
463 {
464 if (i != -1)
465 errno = EINVAL;
466 return -1;
467 }
468
469 /* reset type and size to original values */
470 t->th_buf.typeflag = type2;
471 th_set_size(t, sz2);
472 }
473#endif
474
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500475 th_finish(t);
476
477#ifdef DEBUG
478 /* print tar header */
479 th_print(t);
480#endif
481
482 i = tar_block_write(t, &(t->th_buf));
483 if (i != T_BLOCKSIZE)
484 {
485 if (i != -1)
486 errno = EINVAL;
487 return -1;
488 }
489
490#ifdef DEBUG
491 puts("th_write(): returning 0");
492#endif
493 return 0;
494}
495
496