blob: 81167367e80049f434d09dd65dc53be887496be5 [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>
bigbiff bigbiff9c754052013-01-09 09:09:08 -050014#include <errno.h>
15
16#ifdef STDC_HEADERS
17# include <string.h>
18# include <stdlib.h>
19#endif
20
Ethan Yonkerfefe5912017-09-30 22:22:13 -050021#ifdef HAVE_EXT4_CRYPT
bigbiff7ba75002020-04-11 20:47:09 -040022#include "ext4crypt_tar.h"
23#endif
24
25#ifdef USE_FSCRYPT
26#include "fscrypt_policy.h"
Ethan Yonkerfefe5912017-09-30 22:22:13 -050027#endif
28
bigbiff bigbiff9c754052013-01-09 09:09:08 -050029#define BIT_ISSET(bitmask, bit) ((bitmask) & (bit))
30
Vojtech Bocek25fd68d2013-08-27 03:10:10 +020031// Used to identify selinux_context in extended ('x')
32// metadata. From RedHat implementation.
33#define SELINUX_TAG "RHT.security.selinux="
Ethan Yonker8d039f72017-02-03 14:26:15 -060034#define SELINUX_TAG_LEN strlen(SELINUX_TAG)
bigbiff bigbiff9c754052013-01-09 09:09:08 -050035
Ethan Yonker79f88bd2016-12-09 14:52:12 -060036// Used to identify e4crypt_policy in extended ('x')
37#define E4CRYPT_TAG "TWRP.security.e4crypt="
Ethan Yonker8d039f72017-02-03 14:26:15 -060038#define E4CRYPT_TAG_LEN strlen(E4CRYPT_TAG)
Ethan Yonker79f88bd2016-12-09 14:52:12 -060039
bigbiff7ba75002020-04-11 20:47:09 -040040// Used to identify fscrypt_policy in extended ('x')
41#define FSCRYPT_TAG "TWRP.security.fscrypt="
42#define FSCRYPT_TAG_LEN strlen(FSCRYPT_TAG)
43
Ethan Yonker71187742017-01-13 13:30:10 -060044// Used to identify Posix capabilities in extended ('x')
45#define CAPABILITIES_TAG "SCHILY.xattr.security.capability="
Ethan Yonker8d039f72017-02-03 14:26:15 -060046#define CAPABILITIES_TAG_LEN strlen(CAPABILITIES_TAG)
47
48// Used to identify Android user.default xattr in extended ('x')
49#define ANDROID_USER_DEFAULT_TAG "ANDROID.user.default"
50#define ANDROID_USER_DEFAULT_TAG_LEN strlen(ANDROID_USER_DEFAULT_TAG)
51
52// Used to identify Android user.inode_cache xattr in extended ('x')
53#define ANDROID_USER_CACHE_TAG "ANDROID.user.inode_cache"
54#define ANDROID_USER_CACHE_TAG_LEN strlen(ANDROID_USER_CACHE_TAG)
55
56// Used to identify Android user.inode_code_cache xattr in extended ('x')
57#define ANDROID_USER_CODE_CACHE_TAG "ANDROID.user.inode_code_cache"
58#define ANDROID_USER_CODE_CACHE_TAG_LEN strlen(ANDROID_USER_CODE_CACHE_TAG)
Ethan Yonker71187742017-01-13 13:30:10 -060059
bigbiff bigbiff9c754052013-01-09 09:09:08 -050060/* read a header block */
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -050061/* FIXME: the return value of this function should match the return value
62 of tar_block_read(), which is a macro which references a prototype
63 that returns a ssize_t. So far, this is safe, since tar_block_read()
64 only ever reads 512 (T_BLOCKSIZE) bytes at a time, so any difference
65 in size of ssize_t and int is of negligible risk. BUT, if
66 T_BLOCKSIZE ever changes, or ever becomes a variable parameter
67 controllable by the user, all the code that calls it,
68 including this function and all code that calls it, should be
69 fixed for security reasons.
70 Thanks to Chris Palmer for the critique.
71*/
bigbiff bigbiff9c754052013-01-09 09:09:08 -050072int
73th_read_internal(TAR *t)
74{
75 int i;
76 int num_zero_blocks = 0;
77
78#ifdef DEBUG
79 printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname);
80#endif
81
82 while ((i = tar_block_read(t, &(t->th_buf))) == T_BLOCKSIZE)
83 {
84 /* two all-zero blocks mark EOF */
85 if (t->th_buf.name[0] == '\0')
86 {
87 num_zero_blocks++;
88 if (!BIT_ISSET(t->options, TAR_IGNORE_EOT)
89 && num_zero_blocks >= 2)
90 return 0; /* EOF */
91 else
92 continue;
93 }
94
95 /* verify magic and version */
96 if (BIT_ISSET(t->options, TAR_CHECK_MAGIC)
97 && strncmp(t->th_buf.magic, TMAGIC, TMAGLEN - 1) != 0)
98 {
99#ifdef DEBUG
100 puts("!!! unknown magic value in tar header");
101#endif
102 return -2;
103 }
104
105 if (BIT_ISSET(t->options, TAR_CHECK_VERSION)
106 && strncmp(t->th_buf.version, TVERSION, TVERSLEN) != 0)
107 {
108#ifdef DEBUG
109 puts("!!! unknown version value in tar header");
110#endif
111 return -2;
112 }
113
114 /* check chksum */
115 if (!BIT_ISSET(t->options, TAR_IGNORE_CRC)
116 && !th_crc_ok(t))
117 {
118#ifdef DEBUG
119 puts("!!! tar header checksum error");
120#endif
121 return -2;
122 }
123
124 break;
125 }
126
127#ifdef DEBUG
128 printf("<== th_read_internal(): returning %d\n", i);
129#endif
130 return i;
131}
132
133
134/* wrapper function for th_read_internal() to handle GNU extensions */
135int
136th_read(TAR *t)
137{
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500138 int i;
139 size_t sz, j, blocks;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500140 char *ptr;
141
142#ifdef DEBUG
bigbiff7ba75002020-04-11 20:47:09 -0400143 printf("==> th_read(t=0x%p)\n", (void *)t);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500144#endif
145
146 if (t->th_buf.gnu_longname != NULL)
147 free(t->th_buf.gnu_longname);
148 if (t->th_buf.gnu_longlink != NULL)
149 free(t->th_buf.gnu_longlink);
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200150 if (t->th_buf.selinux_context != NULL)
151 free(t->th_buf.selinux_context);
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600152#ifdef HAVE_EXT4_CRYPT
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500153 if (t->th_buf.eep != NULL)
154 free(t->th_buf.eep);
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600155#endif
bigbiff7ba75002020-04-11 20:47:09 -0400156
157#ifdef USE_FSCRYPT
158 if (t->th_buf.fep != NULL)
159 free(t->th_buf.fep);
160#endif
161
Ethan Yonker71187742017-01-13 13:30:10 -0600162 if (t->th_buf.has_cap_data)
163 {
164 memset(&t->th_buf.cap_data, 0, sizeof(struct vfs_cap_data));
165 t->th_buf.has_cap_data = 0;
166 }
Ethan Yonker8d039f72017-02-03 14:26:15 -0600167 t->th_buf.has_user_default = 0;
168 t->th_buf.has_user_cache = 0;
169 t->th_buf.has_user_code_cache = 0;
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200170
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500171 memset(&(t->th_buf), 0, sizeof(struct tar_header));
172
173 i = th_read_internal(t);
174 if (i == 0)
175 return 1;
176 else if (i != T_BLOCKSIZE)
177 {
178 if (i != -1)
179 errno = EINVAL;
180 return -1;
181 }
182
183 /* check for GNU long link extention */
184 if (TH_ISLONGLINK(t))
185 {
186 sz = th_get_size(t);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500187 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
188 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
189 {
190 errno = E2BIG;
191 return -1;
192 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500193#ifdef DEBUG
194 printf(" th_read(): GNU long linkname detected "
bigbiff7ba75002020-04-11 20:47:09 -0400195 "(%zu bytes, %zu blocks)\n", sz, blocks);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500196#endif
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500197 t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500198 if (t->th_buf.gnu_longlink == NULL)
199 return -1;
200
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500201 for (j = 0, ptr = t->th_buf.gnu_longlink; j < blocks;
202 j++, ptr += T_BLOCKSIZE)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500203 {
204#ifdef DEBUG
205 printf(" th_read(): reading long linkname "
bigbiff7ba75002020-04-11 20:47:09 -0400206 "(%zu blocks left, ptr == %p)\n", blocks-j, (void *) ptr);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500207#endif
208 i = tar_block_read(t, ptr);
209 if (i != T_BLOCKSIZE)
210 {
211 if (i != -1)
212 errno = EINVAL;
213 return -1;
214 }
215#ifdef DEBUG
216 printf(" th_read(): read block == \"%s\"\n", ptr);
217#endif
218 }
219#ifdef DEBUG
220 printf(" th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
221 t->th_buf.gnu_longlink);
222#endif
223
224 i = th_read_internal(t);
225 if (i != T_BLOCKSIZE)
226 {
227 if (i != -1)
228 errno = EINVAL;
229 return -1;
230 }
231 }
232
233 /* check for GNU long name extention */
234 if (TH_ISLONGNAME(t))
235 {
236 sz = th_get_size(t);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500237 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
238 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
239 {
240 errno = E2BIG;
241 return -1;
242 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500243#ifdef DEBUG
244 printf(" th_read(): GNU long filename detected "
bigbiff7ba75002020-04-11 20:47:09 -0400245 "(%zu bytes, %zu blocks)\n", sz, blocks);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500246#endif
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500247 t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500248 if (t->th_buf.gnu_longname == NULL)
249 return -1;
250
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500251 for (j = 0, ptr = t->th_buf.gnu_longname; j < blocks;
252 j++, ptr += T_BLOCKSIZE)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500253 {
254#ifdef DEBUG
255 printf(" th_read(): reading long filename "
bigbiff7ba75002020-04-11 20:47:09 -0400256 "(%zu blocks left, ptr == %p)\n", blocks-j, (void *) ptr);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500257#endif
258 i = tar_block_read(t, ptr);
259 if (i != T_BLOCKSIZE)
260 {
261 if (i != -1)
262 errno = EINVAL;
263 return -1;
264 }
265#ifdef DEBUG
266 printf(" th_read(): read block == \"%s\"\n", ptr);
267#endif
268 }
269#ifdef DEBUG
270 printf(" th_read(): t->th_buf.gnu_longname == \"%s\"\n",
271 t->th_buf.gnu_longname);
272#endif
273
274 i = th_read_internal(t);
275 if (i != T_BLOCKSIZE)
276 {
277 if (i != -1)
278 errno = EINVAL;
279 return -1;
280 }
281 }
282
bigbiff7ba75002020-04-11 20:47:09 -0400283 // Extended headers (selinux contexts, posix file capabilities and encryption policies)
Ethan Yonker71187742017-01-13 13:30:10 -0600284 while(TH_ISEXTHEADER(t) || TH_ISPOLHEADER(t))
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200285 {
286 sz = th_get_size(t);
287
288 if(sz >= T_BLOCKSIZE) // Not supported
289 {
290#ifdef DEBUG
291 printf(" th_read(): Extended header is too long!\n");
292#endif
293 }
294 else
295 {
296 char buf[T_BLOCKSIZE];
297 i = tar_block_read(t, buf);
298 if (i != T_BLOCKSIZE)
299 {
300 if (i != -1)
301 errno = EINVAL;
302 return -1;
303 }
304
305 // To be sure
306 buf[T_BLOCKSIZE-1] = 0;
307
308 int len = strlen(buf);
Ethan Yonker71187742017-01-13 13:30:10 -0600309 // posix capabilities
310 char *start = strstr(buf, CAPABILITIES_TAG);
Ethan Yonker8d039f72017-02-03 14:26:15 -0600311 if (start && start+CAPABILITIES_TAG_LEN < buf+len)
Ethan Yonker71187742017-01-13 13:30:10 -0600312 {
313 start += CAPABILITIES_TAG_LEN;
314 memcpy(&t->th_buf.cap_data, start, sizeof(struct vfs_cap_data));
315 t->th_buf.has_cap_data = 1;
316#ifdef DEBUG
317 printf(" th_read(): Posix capabilities detected\n");
318#endif
319 } // end posix capabilities
Matt Mower87413642017-01-17 21:14:46 -0600320 // selinux contexts
Ethan Yonker71187742017-01-13 13:30:10 -0600321 start = strstr(buf, SELINUX_TAG);
Ethan Yonker8d039f72017-02-03 14:26:15 -0600322 if (start && start+SELINUX_TAG_LEN < buf+len)
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200323 {
324 start += SELINUX_TAG_LEN;
325 char *end = strchr(start, '\n');
326 if(end)
327 {
328 t->th_buf.selinux_context = strndup(start, end-start);
329#ifdef DEBUG
330 printf(" th_read(): SELinux context xattr detected: %s\n", t->th_buf.selinux_context);
331#endif
332 }
Matt Mower87413642017-01-17 21:14:46 -0600333 } // end selinux contexts
Ethan Yonker8d039f72017-02-03 14:26:15 -0600334 // android user.default xattr
335 start = strstr(buf, ANDROID_USER_DEFAULT_TAG);
336 if (start)
337 {
338 t->th_buf.has_user_default = 1;
339#ifdef DEBUG
340 printf(" th_read(): android user.default xattr detected\n");
341#endif
342 } // end android user.default xattr
343 // android user.inode_cache xattr
344 start = strstr(buf, ANDROID_USER_CACHE_TAG);
345 if (start)
346 {
347 t->th_buf.has_user_cache = 1;
348#ifdef DEBUG
349 printf(" th_read(): android user.inode_cache xattr detected\n");
350#endif
351 } // end android user.inode_cache xattr
352 // android user.inode_code_cache xattr
353 start = strstr(buf, ANDROID_USER_CODE_CACHE_TAG);
354 if (start)
355 {
356 t->th_buf.has_user_code_cache = 1;
357#ifdef DEBUG
358 printf(" th_read(): android user.inode_code_cache xattr detected\n");
359#endif
360 } // end android user.inode_code_cache xattr
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600361#ifdef HAVE_EXT4_CRYPT
Ethan Yonker71187742017-01-13 13:30:10 -0600362 start = strstr(buf, E4CRYPT_TAG);
Ethan Yonker8d039f72017-02-03 14:26:15 -0600363 if (start && start+E4CRYPT_TAG_LEN < buf+len)
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600364 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500365 t->th_buf.eep = (struct ext4_encryption_policy*)malloc(sizeof(struct ext4_encryption_policy));
366 if (!t->th_buf.eep) {
367 printf("malloc ext4_encryption_policy\n");
368 return -1;
369 }
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600370 start += E4CRYPT_TAG_LEN;
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500371 if (*start == '2')
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600372 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500373 start++;
Ethan Yonker58f21322018-08-24 11:17:36 -0500374 char *newline_check = start + sizeof(struct ext4_encryption_policy);
375 if (*newline_check != '\n')
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500376 printf("did not find newline char in expected location, continuing anyway...\n");
377 memcpy(t->th_buf.eep, start, sizeof(struct ext4_encryption_policy));
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600378#ifdef DEBUG
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500379 printf(" th_read(): E4Crypt policy v2 detected: %i %i %i %i %s\n",
380 (int)t->th_buf.eep->version,
381 (int)t->th_buf.eep->contents_encryption_mode,
382 (int)t->th_buf.eep->filenames_encryption_mode,
383 (int)t->th_buf.eep->flags,
384 t->th_buf.eep->master_key_descriptor);
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600385#endif
386 }
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500387 else
388 {
389 e4crypt_policy_fill_default_struct(t->th_buf.eep);
390 char *end = strchr(start, '\n');
391 if(!end)
392 end = strchr(start, '\0');
393 if(end)
394 {
395 strncpy(t->th_buf.eep->master_key_descriptor, start, end-start);
396#ifdef DEBUG
397 printf(" th_read(): E4Crypt policy v1 detected: %s\n", t->th_buf.eep->master_key_descriptor);
398#endif
399 }
400 }
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600401 }
Ethan Yonker71187742017-01-13 13:30:10 -0600402#endif // HAVE_EXT4_CRYPT
bigbiff7ba75002020-04-11 20:47:09 -0400403
404#ifdef USE_FSCRYPT
405 start = strstr(buf, FSCRYPT_TAG);
406 if (start && start+FSCRYPT_TAG_LEN < buf+len) {
407 t->th_buf.fep = (struct fscrypt_encryption_policy*)malloc(sizeof(struct fscrypt_encryption_policy));
408 if (!t->th_buf.fep) {
409 printf("malloc fscrypt_encryption_policy\n");
410 return -1;
411 }
412 start += FSCRYPT_TAG_LEN;
413 if (*start == '0') {
414 start++;
415 char *newline_check = start + sizeof(struct fscrypt_encryption_policy);
416 if (*newline_check != '\n')
417 printf("did not find newline char in expected location, continuing anyway...\n");
418 memcpy(t->th_buf.fep, start, sizeof(struct fscrypt_encryption_policy));
419#ifdef DEBUG
420 printf(" th_read(): FSCrypt policy v1 detected: %i %i %i %i %s\n",
421 (int)t->th_buf.fep->version,
422 (int)t->th_buf.fep->contents_encryption_mode,
423 (int)t->th_buf.fep->filenames_encryption_mode,
424 (int)t->th_buf.fep->flags,
425 t->th_buf.fep->master_key_descriptor);
426#endif
427 }
428 else {
429 printf(" invalid fscrypt header found\n");
430 }
431 }
432#endif // USE_FSCRYPT
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600433 }
434
435 i = th_read_internal(t);
436 if (i != T_BLOCKSIZE)
437 {
438 if (i != -1)
439 errno = EINVAL;
440 return -1;
441 }
442 }
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600443
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500444 return 0;
445}
446
Ethan Yonker71187742017-01-13 13:30:10 -0600447/* write an extended block */
448static int
449th_write_extended(TAR *t, char* buf, uint64_t sz)
450{
451 char type2;
452 uint64_t sz2;
453 int i;
454
455 /* save old size and type */
456 type2 = t->th_buf.typeflag;
457 sz2 = th_get_size(t);
458
459 /* write out initial header block with fake size and type */
460 t->th_buf.typeflag = TH_EXT_TYPE;
461
462 if(sz >= T_BLOCKSIZE) // impossible
463 {
464 errno = EINVAL;
465 return -1;
466 }
467
468 th_set_size(t, sz);
469 th_finish(t);
470 i = tar_block_write(t, &(t->th_buf));
471 if (i != T_BLOCKSIZE)
472 {
473 if (i != -1)
474 errno = EINVAL;
475 return -1;
476 }
477
478 i = tar_block_write(t, buf);
479 if (i != T_BLOCKSIZE)
480 {
481 if (i != -1)
482 errno = EINVAL;
483 return -1;
484 }
485
486 /* reset type and size to original values */
487 t->th_buf.typeflag = type2;
488 th_set_size(t, sz2);
489 memset(buf, 0, T_BLOCKSIZE);
490 return 0;
491}
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500492
493/* write a header block */
494int
495th_write(TAR *t)
496{
497 int i, j;
498 char type2;
Ethan Yonker71187742017-01-13 13:30:10 -0600499 uint64_t sz, sz2, total_sz = 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500500 char *ptr;
501 char buf[T_BLOCKSIZE];
502
503#ifdef DEBUG
504 printf("==> th_write(TAR=\"%s\")\n", t->pathname);
505 th_print(t);
506#endif
507
508 if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
509 {
510#ifdef DEBUG
511 printf("th_write(): using gnu_longlink (\"%s\")\n",
512 t->th_buf.gnu_longlink);
513#endif
514 /* save old size and type */
515 type2 = t->th_buf.typeflag;
516 sz2 = th_get_size(t);
517
518 /* write out initial header block with fake size and type */
519 t->th_buf.typeflag = GNU_LONGLINK_TYPE;
520 sz = strlen(t->th_buf.gnu_longlink);
521 th_set_size(t, sz);
522 th_finish(t);
523 i = tar_block_write(t, &(t->th_buf));
524 if (i != T_BLOCKSIZE)
525 {
526 if (i != -1)
527 errno = EINVAL;
528 return -1;
529 }
530
531 /* write out extra blocks containing long name */
532 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
533 ptr = t->th_buf.gnu_longlink; j > 1;
534 j--, ptr += T_BLOCKSIZE)
535 {
536 i = tar_block_write(t, ptr);
537 if (i != T_BLOCKSIZE)
538 {
539 if (i != -1)
540 errno = EINVAL;
541 return -1;
542 }
543 }
544 memset(buf, 0, T_BLOCKSIZE);
545 strncpy(buf, ptr, T_BLOCKSIZE);
546 i = tar_block_write(t, &buf);
547 if (i != T_BLOCKSIZE)
548 {
549 if (i != -1)
550 errno = EINVAL;
551 return -1;
552 }
553
554 /* reset type and size to original values */
555 t->th_buf.typeflag = type2;
556 th_set_size(t, sz2);
557 }
558
559 if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
560 {
561#ifdef DEBUG
562 printf("th_write(): using gnu_longname (\"%s\")\n",
563 t->th_buf.gnu_longname);
564#endif
565 /* save old size and type */
566 type2 = t->th_buf.typeflag;
567 sz2 = th_get_size(t);
568
569 /* write out initial header block with fake size and type */
570 t->th_buf.typeflag = GNU_LONGNAME_TYPE;
571 sz = strlen(t->th_buf.gnu_longname);
572 th_set_size(t, sz);
573 th_finish(t);
574 i = tar_block_write(t, &(t->th_buf));
575 if (i != T_BLOCKSIZE)
576 {
577 if (i != -1)
578 errno = EINVAL;
579 return -1;
580 }
581
582 /* write out extra blocks containing long name */
583 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
584 ptr = t->th_buf.gnu_longname; j > 1;
585 j--, ptr += T_BLOCKSIZE)
586 {
587 i = tar_block_write(t, ptr);
588 if (i != T_BLOCKSIZE)
589 {
590 if (i != -1)
591 errno = EINVAL;
592 return -1;
593 }
594 }
595 memset(buf, 0, T_BLOCKSIZE);
596 strncpy(buf, ptr, T_BLOCKSIZE);
597 i = tar_block_write(t, &buf);
598 if (i != T_BLOCKSIZE)
599 {
600 if (i != -1)
601 errno = EINVAL;
602 return -1;
603 }
604
605 /* reset type and size to original values */
606 t->th_buf.typeflag = type2;
607 th_set_size(t, sz2);
608 }
609
Ethan Yonker71187742017-01-13 13:30:10 -0600610 memset(buf, 0, T_BLOCKSIZE);
611 ptr = buf;
Matt Mower87413642017-01-17 21:14:46 -0600612
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200613 if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
614 {
615#ifdef DEBUG
616 printf("th_write(): using selinux_context (\"%s\")\n",
617 t->th_buf.selinux_context);
618#endif
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200619 /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */
620 // size newline
621 sz = SELINUX_TAG_LEN + strlen(t->th_buf.selinux_context) + 3 + 1;
622
623 if(sz >= 100) // another ascci digit for size
624 ++sz;
625
Ethan Yonker71187742017-01-13 13:30:10 -0600626 total_sz += sz;
627 snprintf(ptr, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", (int)sz, t->th_buf.selinux_context);
628 ptr += sz;
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200629 }
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200630
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600631#ifdef HAVE_EXT4_CRYPT
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500632 if((t->options & TAR_STORE_EXT4_POL) && t->th_buf.eep != NULL)
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600633 {
634#ifdef DEBUG
635 printf("th_write(): using e4crypt_policy %s\n",
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500636 t->th_buf.eep->master_key_descriptor);
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600637#endif
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500638 /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *version code* *content* *newline* */
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600639 // size newline
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500640 sz = E4CRYPT_TAG_LEN + sizeof(struct ext4_encryption_policy) + 1 + 3 + 1;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600641
642 if(sz >= 100) // another ascci digit for size
643 ++sz;
644
Ethan Yonker71187742017-01-13 13:30:10 -0600645 if (total_sz + sz >= T_BLOCKSIZE)
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600646 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500647 if (th_write_extended(t, &buf[0], total_sz))
Ethan Yonker71187742017-01-13 13:30:10 -0600648 return -1;
649 ptr = buf;
650 total_sz = sz;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600651 }
Ethan Yonker71187742017-01-13 13:30:10 -0600652 else
653 total_sz += sz;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600654
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500655 snprintf(ptr, T_BLOCKSIZE, "%d "E4CRYPT_TAG"2", (int)sz);
656 memcpy(ptr + sz - sizeof(struct ext4_encryption_policy) - 1, t->th_buf.eep, sizeof(struct ext4_encryption_policy));
Ethan Yonker71187742017-01-13 13:30:10 -0600657 char *nlptr = ptr + sz - 1;
658 *nlptr = '\n';
659 ptr += sz;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600660 }
661#endif
662
bigbiff7ba75002020-04-11 20:47:09 -0400663#ifdef USE_FSCRYPT
664 if((t->options & TAR_STORE_FSCRYPT_POL) && t->th_buf.fep != NULL)
665 {
666#ifdef DEBUG
667 printf("th_write(): using fscrypt_policy %s\n",
668 t->th_buf.fep->master_key_descriptor);
669#endif
670 /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *version code* *content* *newline* */
671 // size newline
672 sz = FSCRYPT_TAG_LEN + sizeof(struct fscrypt_encryption_policy) + 1 + 3 + 1;
673
674 if(sz >= 100) // another ascci digit for size
675 ++sz;
676
677 if (total_sz + sz >= T_BLOCKSIZE)
678 {
679 if (th_write_extended(t, &buf[0], total_sz))
680 return -1;
681 ptr = buf;
682 total_sz = sz;
683 }
684 else
685 total_sz += sz;
686
687 snprintf(ptr, T_BLOCKSIZE, "%d "FSCRYPT_TAG"0", (int)sz);
688 memcpy(ptr + sz - sizeof(struct fscrypt_encryption_policy) - 1, t->th_buf.fep, sizeof(struct fscrypt_encryption_policy));
689 char *nlptr = ptr + sz - 1;
690 *nlptr = '\n';
691 ptr += sz;
692 }
693#endif
694
Ethan Yonker71187742017-01-13 13:30:10 -0600695 if((t->options & TAR_STORE_POSIX_CAP) && t->th_buf.has_cap_data)
696 {
697#ifdef DEBUG
698 printf("th_write(): has a posix capability\n");
699#endif
700 sz = CAPABILITIES_TAG_LEN + sizeof(struct vfs_cap_data) + 3 + 1;
701
702 if(sz >= 100) // another ascci digit for size
703 ++sz;
704
705 if (total_sz + sz >= T_BLOCKSIZE)
706 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500707 if (th_write_extended(t, &buf[0], total_sz))
Ethan Yonker71187742017-01-13 13:30:10 -0600708 return -1;
709 ptr = buf;
710 total_sz = sz;
711 }
712 else
713 total_sz += sz;
714
715 snprintf(ptr, T_BLOCKSIZE, "%d "CAPABILITIES_TAG, (int)sz);
716 memcpy(ptr + CAPABILITIES_TAG_LEN + 3, &t->th_buf.cap_data, sizeof(struct vfs_cap_data));
717 char *nlptr = ptr + sz - 1;
718 *nlptr = '\n';
719 ptr += sz;
720 }
Ethan Yonker8d039f72017-02-03 14:26:15 -0600721 if (t->options & TAR_STORE_ANDROID_USER_XATTR)
722 {
723 if (t->th_buf.has_user_default) {
724#ifdef DEBUG
725 printf("th_write(): has android user.default xattr\n");
726#endif
727 sz = ANDROID_USER_DEFAULT_TAG_LEN + 3 + 1;
728
729 if (total_sz + sz >= T_BLOCKSIZE)
730 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500731 if (th_write_extended(t, &buf[0], total_sz))
Ethan Yonker8d039f72017-02-03 14:26:15 -0600732 return -1;
733 ptr = buf;
734 total_sz = sz;
735 }
736 else
737 total_sz += sz;
738
739 snprintf(ptr, T_BLOCKSIZE, "%d "ANDROID_USER_DEFAULT_TAG, (int)sz);
740 char *nlptr = ptr + sz - 1;
741 *nlptr = '\n';
742 ptr += sz;
743 }
744 if (t->th_buf.has_user_cache) {
745#ifdef DEBUG
746 printf("th_write(): has android user.inode_cache xattr\n");
747#endif
748 sz = ANDROID_USER_CACHE_TAG_LEN + 3 + 1;
749
750 if (total_sz + sz >= T_BLOCKSIZE)
751 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500752 if (th_write_extended(t, &buf[0], total_sz))
Ethan Yonker8d039f72017-02-03 14:26:15 -0600753 return -1;
754 ptr = buf;
755 total_sz = sz;
756 }
757 else
758 total_sz += sz;
759
760 snprintf(ptr, T_BLOCKSIZE, "%d "ANDROID_USER_CACHE_TAG, (int)sz);
761 char *nlptr = ptr + sz - 1;
762 *nlptr = '\n';
763 ptr += sz;
764 }
765 if (t->th_buf.has_user_code_cache) {
766#ifdef DEBUG
767 printf("th_write(): has android user.inode_code_cache xattr\n");
768#endif
769 sz = ANDROID_USER_CODE_CACHE_TAG_LEN + 3 + 1;
770
771 if (total_sz + sz >= T_BLOCKSIZE)
772 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500773 if (th_write_extended(t, &buf[0], total_sz))
Ethan Yonker8d039f72017-02-03 14:26:15 -0600774 return -1;
775 ptr = buf;
776 total_sz = sz;
777 }
778 else
779 total_sz += sz;
780
781 snprintf(ptr, T_BLOCKSIZE, "%d "ANDROID_USER_CODE_CACHE_TAG, (int)sz);
782 char *nlptr = ptr + sz - 1;
783 *nlptr = '\n';
784 ptr += sz;
785 }
786 }
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500787 if (total_sz > 0 && th_write_extended(t, &buf[0], total_sz)) // write any outstanding tar extended header
Ethan Yonker71187742017-01-13 13:30:10 -0600788 return -1;
789
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500790 th_finish(t);
791
792#ifdef DEBUG
793 /* print tar header */
794 th_print(t);
795#endif
796
797 i = tar_block_write(t, &(t->th_buf));
798 if (i != T_BLOCKSIZE)
799 {
800 if (i != -1)
801 errno = EINVAL;
802 return -1;
803 }
804
805#ifdef DEBUG
806 puts("th_write(): returning 0");
807#endif
808 return 0;
809}
810
811