blob: 2f1004ba9a6a7877226efa1a0cc66189cf25f296 [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
bigbiff7ba75002020-04-11 20:47:09 -040021#define DEBUG 1
22
Ethan Yonkerfefe5912017-09-30 22:22:13 -050023#ifdef HAVE_EXT4_CRYPT
bigbiff7ba75002020-04-11 20:47:09 -040024#include "ext4crypt_tar.h"
25#endif
26
27#ifdef USE_FSCRYPT
28#include "fscrypt_policy.h"
Ethan Yonkerfefe5912017-09-30 22:22:13 -050029#endif
30
bigbiff bigbiff9c754052013-01-09 09:09:08 -050031#define BIT_ISSET(bitmask, bit) ((bitmask) & (bit))
32
Vojtech Bocek25fd68d2013-08-27 03:10:10 +020033// Used to identify selinux_context in extended ('x')
34// metadata. From RedHat implementation.
35#define SELINUX_TAG "RHT.security.selinux="
Ethan Yonker8d039f72017-02-03 14:26:15 -060036#define SELINUX_TAG_LEN strlen(SELINUX_TAG)
bigbiff bigbiff9c754052013-01-09 09:09:08 -050037
Ethan Yonker79f88bd2016-12-09 14:52:12 -060038// Used to identify e4crypt_policy in extended ('x')
39#define E4CRYPT_TAG "TWRP.security.e4crypt="
Ethan Yonker8d039f72017-02-03 14:26:15 -060040#define E4CRYPT_TAG_LEN strlen(E4CRYPT_TAG)
Ethan Yonker79f88bd2016-12-09 14:52:12 -060041
bigbiff7ba75002020-04-11 20:47:09 -040042// Used to identify fscrypt_policy in extended ('x')
43#define FSCRYPT_TAG "TWRP.security.fscrypt="
44#define FSCRYPT_TAG_LEN strlen(FSCRYPT_TAG)
45
Ethan Yonker71187742017-01-13 13:30:10 -060046// Used to identify Posix capabilities in extended ('x')
47#define CAPABILITIES_TAG "SCHILY.xattr.security.capability="
Ethan Yonker8d039f72017-02-03 14:26:15 -060048#define CAPABILITIES_TAG_LEN strlen(CAPABILITIES_TAG)
49
50// Used to identify Android user.default xattr in extended ('x')
51#define ANDROID_USER_DEFAULT_TAG "ANDROID.user.default"
52#define ANDROID_USER_DEFAULT_TAG_LEN strlen(ANDROID_USER_DEFAULT_TAG)
53
54// Used to identify Android user.inode_cache xattr in extended ('x')
55#define ANDROID_USER_CACHE_TAG "ANDROID.user.inode_cache"
56#define ANDROID_USER_CACHE_TAG_LEN strlen(ANDROID_USER_CACHE_TAG)
57
58// Used to identify Android user.inode_code_cache xattr in extended ('x')
59#define ANDROID_USER_CODE_CACHE_TAG "ANDROID.user.inode_code_cache"
60#define ANDROID_USER_CODE_CACHE_TAG_LEN strlen(ANDROID_USER_CODE_CACHE_TAG)
Ethan Yonker71187742017-01-13 13:30:10 -060061
bigbiff bigbiff9c754052013-01-09 09:09:08 -050062/* read a header block */
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -050063/* FIXME: the return value of this function should match the return value
64 of tar_block_read(), which is a macro which references a prototype
65 that returns a ssize_t. So far, this is safe, since tar_block_read()
66 only ever reads 512 (T_BLOCKSIZE) bytes at a time, so any difference
67 in size of ssize_t and int is of negligible risk. BUT, if
68 T_BLOCKSIZE ever changes, or ever becomes a variable parameter
69 controllable by the user, all the code that calls it,
70 including this function and all code that calls it, should be
71 fixed for security reasons.
72 Thanks to Chris Palmer for the critique.
73*/
bigbiff bigbiff9c754052013-01-09 09:09:08 -050074int
75th_read_internal(TAR *t)
76{
77 int i;
78 int num_zero_blocks = 0;
79
80#ifdef DEBUG
81 printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname);
82#endif
83
84 while ((i = tar_block_read(t, &(t->th_buf))) == T_BLOCKSIZE)
85 {
86 /* two all-zero blocks mark EOF */
87 if (t->th_buf.name[0] == '\0')
88 {
89 num_zero_blocks++;
90 if (!BIT_ISSET(t->options, TAR_IGNORE_EOT)
91 && num_zero_blocks >= 2)
92 return 0; /* EOF */
93 else
94 continue;
95 }
96
97 /* verify magic and version */
98 if (BIT_ISSET(t->options, TAR_CHECK_MAGIC)
99 && strncmp(t->th_buf.magic, TMAGIC, TMAGLEN - 1) != 0)
100 {
101#ifdef DEBUG
102 puts("!!! unknown magic value in tar header");
103#endif
104 return -2;
105 }
106
107 if (BIT_ISSET(t->options, TAR_CHECK_VERSION)
108 && strncmp(t->th_buf.version, TVERSION, TVERSLEN) != 0)
109 {
110#ifdef DEBUG
111 puts("!!! unknown version value in tar header");
112#endif
113 return -2;
114 }
115
116 /* check chksum */
117 if (!BIT_ISSET(t->options, TAR_IGNORE_CRC)
118 && !th_crc_ok(t))
119 {
120#ifdef DEBUG
121 puts("!!! tar header checksum error");
122#endif
123 return -2;
124 }
125
126 break;
127 }
128
129#ifdef DEBUG
130 printf("<== th_read_internal(): returning %d\n", i);
131#endif
132 return i;
133}
134
135
136/* wrapper function for th_read_internal() to handle GNU extensions */
137int
138th_read(TAR *t)
139{
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500140 int i;
141 size_t sz, j, blocks;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500142 char *ptr;
143
144#ifdef DEBUG
bigbiff7ba75002020-04-11 20:47:09 -0400145 printf("==> th_read(t=0x%p)\n", (void *)t);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500146#endif
147
148 if (t->th_buf.gnu_longname != NULL)
149 free(t->th_buf.gnu_longname);
150 if (t->th_buf.gnu_longlink != NULL)
151 free(t->th_buf.gnu_longlink);
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200152 if (t->th_buf.selinux_context != NULL)
153 free(t->th_buf.selinux_context);
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600154#ifdef HAVE_EXT4_CRYPT
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500155 if (t->th_buf.eep != NULL)
156 free(t->th_buf.eep);
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600157#endif
bigbiff7ba75002020-04-11 20:47:09 -0400158
159#ifdef USE_FSCRYPT
160 if (t->th_buf.fep != NULL)
161 free(t->th_buf.fep);
162#endif
163
Ethan Yonker71187742017-01-13 13:30:10 -0600164 if (t->th_buf.has_cap_data)
165 {
166 memset(&t->th_buf.cap_data, 0, sizeof(struct vfs_cap_data));
167 t->th_buf.has_cap_data = 0;
168 }
Ethan Yonker8d039f72017-02-03 14:26:15 -0600169 t->th_buf.has_user_default = 0;
170 t->th_buf.has_user_cache = 0;
171 t->th_buf.has_user_code_cache = 0;
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200172
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500173 memset(&(t->th_buf), 0, sizeof(struct tar_header));
174
175 i = th_read_internal(t);
176 if (i == 0)
177 return 1;
178 else if (i != T_BLOCKSIZE)
179 {
180 if (i != -1)
181 errno = EINVAL;
182 return -1;
183 }
184
185 /* check for GNU long link extention */
186 if (TH_ISLONGLINK(t))
187 {
188 sz = th_get_size(t);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500189 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
190 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
191 {
192 errno = E2BIG;
193 return -1;
194 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500195#ifdef DEBUG
196 printf(" th_read(): GNU long linkname detected "
bigbiff7ba75002020-04-11 20:47:09 -0400197 "(%zu bytes, %zu blocks)\n", sz, blocks);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500198#endif
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500199 t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500200 if (t->th_buf.gnu_longlink == NULL)
201 return -1;
202
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500203 for (j = 0, ptr = t->th_buf.gnu_longlink; j < blocks;
204 j++, ptr += T_BLOCKSIZE)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500205 {
206#ifdef DEBUG
207 printf(" th_read(): reading long linkname "
bigbiff7ba75002020-04-11 20:47:09 -0400208 "(%zu blocks left, ptr == %p)\n", blocks-j, (void *) ptr);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500209#endif
210 i = tar_block_read(t, ptr);
211 if (i != T_BLOCKSIZE)
212 {
213 if (i != -1)
214 errno = EINVAL;
215 return -1;
216 }
217#ifdef DEBUG
218 printf(" th_read(): read block == \"%s\"\n", ptr);
219#endif
220 }
221#ifdef DEBUG
222 printf(" th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
223 t->th_buf.gnu_longlink);
224#endif
225
226 i = th_read_internal(t);
227 if (i != T_BLOCKSIZE)
228 {
229 if (i != -1)
230 errno = EINVAL;
231 return -1;
232 }
233 }
234
235 /* check for GNU long name extention */
236 if (TH_ISLONGNAME(t))
237 {
238 sz = th_get_size(t);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500239 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
240 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
241 {
242 errno = E2BIG;
243 return -1;
244 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500245#ifdef DEBUG
246 printf(" th_read(): GNU long filename detected "
bigbiff7ba75002020-04-11 20:47:09 -0400247 "(%zu bytes, %zu blocks)\n", sz, blocks);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500248#endif
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500249 t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500250 if (t->th_buf.gnu_longname == NULL)
251 return -1;
252
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500253 for (j = 0, ptr = t->th_buf.gnu_longname; j < blocks;
254 j++, ptr += T_BLOCKSIZE)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500255 {
256#ifdef DEBUG
257 printf(" th_read(): reading long filename "
bigbiff7ba75002020-04-11 20:47:09 -0400258 "(%zu blocks left, ptr == %p)\n", blocks-j, (void *) ptr);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500259#endif
260 i = tar_block_read(t, ptr);
261 if (i != T_BLOCKSIZE)
262 {
263 if (i != -1)
264 errno = EINVAL;
265 return -1;
266 }
267#ifdef DEBUG
268 printf(" th_read(): read block == \"%s\"\n", ptr);
269#endif
270 }
271#ifdef DEBUG
272 printf(" th_read(): t->th_buf.gnu_longname == \"%s\"\n",
273 t->th_buf.gnu_longname);
274#endif
275
276 i = th_read_internal(t);
277 if (i != T_BLOCKSIZE)
278 {
279 if (i != -1)
280 errno = EINVAL;
281 return -1;
282 }
283 }
284
bigbiff7ba75002020-04-11 20:47:09 -0400285 // Extended headers (selinux contexts, posix file capabilities and encryption policies)
Ethan Yonker71187742017-01-13 13:30:10 -0600286 while(TH_ISEXTHEADER(t) || TH_ISPOLHEADER(t))
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200287 {
288 sz = th_get_size(t);
289
290 if(sz >= T_BLOCKSIZE) // Not supported
291 {
292#ifdef DEBUG
293 printf(" th_read(): Extended header is too long!\n");
294#endif
295 }
296 else
297 {
298 char buf[T_BLOCKSIZE];
299 i = tar_block_read(t, buf);
300 if (i != T_BLOCKSIZE)
301 {
302 if (i != -1)
303 errno = EINVAL;
304 return -1;
305 }
306
307 // To be sure
308 buf[T_BLOCKSIZE-1] = 0;
309
310 int len = strlen(buf);
Ethan Yonker71187742017-01-13 13:30:10 -0600311 // posix capabilities
312 char *start = strstr(buf, CAPABILITIES_TAG);
Ethan Yonker8d039f72017-02-03 14:26:15 -0600313 if (start && start+CAPABILITIES_TAG_LEN < buf+len)
Ethan Yonker71187742017-01-13 13:30:10 -0600314 {
315 start += CAPABILITIES_TAG_LEN;
316 memcpy(&t->th_buf.cap_data, start, sizeof(struct vfs_cap_data));
317 t->th_buf.has_cap_data = 1;
318#ifdef DEBUG
319 printf(" th_read(): Posix capabilities detected\n");
320#endif
321 } // end posix capabilities
Matt Mower87413642017-01-17 21:14:46 -0600322 // selinux contexts
Ethan Yonker71187742017-01-13 13:30:10 -0600323 start = strstr(buf, SELINUX_TAG);
Ethan Yonker8d039f72017-02-03 14:26:15 -0600324 if (start && start+SELINUX_TAG_LEN < buf+len)
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200325 {
326 start += SELINUX_TAG_LEN;
327 char *end = strchr(start, '\n');
328 if(end)
329 {
330 t->th_buf.selinux_context = strndup(start, end-start);
331#ifdef DEBUG
332 printf(" th_read(): SELinux context xattr detected: %s\n", t->th_buf.selinux_context);
333#endif
334 }
Matt Mower87413642017-01-17 21:14:46 -0600335 } // end selinux contexts
Ethan Yonker8d039f72017-02-03 14:26:15 -0600336 // android user.default xattr
337 start = strstr(buf, ANDROID_USER_DEFAULT_TAG);
338 if (start)
339 {
340 t->th_buf.has_user_default = 1;
341#ifdef DEBUG
342 printf(" th_read(): android user.default xattr detected\n");
343#endif
344 } // end android user.default xattr
345 // android user.inode_cache xattr
346 start = strstr(buf, ANDROID_USER_CACHE_TAG);
347 if (start)
348 {
349 t->th_buf.has_user_cache = 1;
350#ifdef DEBUG
351 printf(" th_read(): android user.inode_cache xattr detected\n");
352#endif
353 } // end android user.inode_cache xattr
354 // android user.inode_code_cache xattr
355 start = strstr(buf, ANDROID_USER_CODE_CACHE_TAG);
356 if (start)
357 {
358 t->th_buf.has_user_code_cache = 1;
359#ifdef DEBUG
360 printf(" th_read(): android user.inode_code_cache xattr detected\n");
361#endif
362 } // end android user.inode_code_cache xattr
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600363#ifdef HAVE_EXT4_CRYPT
Ethan Yonker71187742017-01-13 13:30:10 -0600364 start = strstr(buf, E4CRYPT_TAG);
Ethan Yonker8d039f72017-02-03 14:26:15 -0600365 if (start && start+E4CRYPT_TAG_LEN < buf+len)
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600366 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500367 t->th_buf.eep = (struct ext4_encryption_policy*)malloc(sizeof(struct ext4_encryption_policy));
368 if (!t->th_buf.eep) {
369 printf("malloc ext4_encryption_policy\n");
370 return -1;
371 }
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600372 start += E4CRYPT_TAG_LEN;
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500373 if (*start == '2')
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600374 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500375 start++;
Ethan Yonker58f21322018-08-24 11:17:36 -0500376 char *newline_check = start + sizeof(struct ext4_encryption_policy);
377 if (*newline_check != '\n')
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500378 printf("did not find newline char in expected location, continuing anyway...\n");
379 memcpy(t->th_buf.eep, start, sizeof(struct ext4_encryption_policy));
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600380#ifdef DEBUG
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500381 printf(" th_read(): E4Crypt policy v2 detected: %i %i %i %i %s\n",
382 (int)t->th_buf.eep->version,
383 (int)t->th_buf.eep->contents_encryption_mode,
384 (int)t->th_buf.eep->filenames_encryption_mode,
385 (int)t->th_buf.eep->flags,
386 t->th_buf.eep->master_key_descriptor);
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600387#endif
388 }
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500389 else
390 {
391 e4crypt_policy_fill_default_struct(t->th_buf.eep);
392 char *end = strchr(start, '\n');
393 if(!end)
394 end = strchr(start, '\0');
395 if(end)
396 {
397 strncpy(t->th_buf.eep->master_key_descriptor, start, end-start);
398#ifdef DEBUG
399 printf(" th_read(): E4Crypt policy v1 detected: %s\n", t->th_buf.eep->master_key_descriptor);
400#endif
401 }
402 }
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600403 }
Ethan Yonker71187742017-01-13 13:30:10 -0600404#endif // HAVE_EXT4_CRYPT
bigbiff7ba75002020-04-11 20:47:09 -0400405
406#ifdef USE_FSCRYPT
407 start = strstr(buf, FSCRYPT_TAG);
408 if (start && start+FSCRYPT_TAG_LEN < buf+len) {
409 t->th_buf.fep = (struct fscrypt_encryption_policy*)malloc(sizeof(struct fscrypt_encryption_policy));
410 if (!t->th_buf.fep) {
411 printf("malloc fscrypt_encryption_policy\n");
412 return -1;
413 }
414 start += FSCRYPT_TAG_LEN;
415 if (*start == '0') {
416 start++;
417 char *newline_check = start + sizeof(struct fscrypt_encryption_policy);
418 if (*newline_check != '\n')
419 printf("did not find newline char in expected location, continuing anyway...\n");
420 memcpy(t->th_buf.fep, start, sizeof(struct fscrypt_encryption_policy));
421#ifdef DEBUG
422 printf(" th_read(): FSCrypt policy v1 detected: %i %i %i %i %s\n",
423 (int)t->th_buf.fep->version,
424 (int)t->th_buf.fep->contents_encryption_mode,
425 (int)t->th_buf.fep->filenames_encryption_mode,
426 (int)t->th_buf.fep->flags,
427 t->th_buf.fep->master_key_descriptor);
428#endif
429 }
430 else {
431 printf(" invalid fscrypt header found\n");
432 }
433 }
434#endif // USE_FSCRYPT
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600435 }
436
437 i = th_read_internal(t);
438 if (i != T_BLOCKSIZE)
439 {
440 if (i != -1)
441 errno = EINVAL;
442 return -1;
443 }
444 }
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600445
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500446 return 0;
447}
448
Ethan Yonker71187742017-01-13 13:30:10 -0600449/* write an extended block */
450static int
451th_write_extended(TAR *t, char* buf, uint64_t sz)
452{
453 char type2;
454 uint64_t sz2;
455 int i;
456
457 /* save old size and type */
458 type2 = t->th_buf.typeflag;
459 sz2 = th_get_size(t);
460
461 /* write out initial header block with fake size and type */
462 t->th_buf.typeflag = TH_EXT_TYPE;
463
464 if(sz >= T_BLOCKSIZE) // impossible
465 {
466 errno = EINVAL;
467 return -1;
468 }
469
470 th_set_size(t, sz);
471 th_finish(t);
472 i = tar_block_write(t, &(t->th_buf));
473 if (i != T_BLOCKSIZE)
474 {
475 if (i != -1)
476 errno = EINVAL;
477 return -1;
478 }
479
480 i = tar_block_write(t, buf);
481 if (i != T_BLOCKSIZE)
482 {
483 if (i != -1)
484 errno = EINVAL;
485 return -1;
486 }
487
488 /* reset type and size to original values */
489 t->th_buf.typeflag = type2;
490 th_set_size(t, sz2);
491 memset(buf, 0, T_BLOCKSIZE);
492 return 0;
493}
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500494
495/* write a header block */
496int
497th_write(TAR *t)
498{
499 int i, j;
500 char type2;
Ethan Yonker71187742017-01-13 13:30:10 -0600501 uint64_t sz, sz2, total_sz = 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500502 char *ptr;
503 char buf[T_BLOCKSIZE];
504
505#ifdef DEBUG
506 printf("==> th_write(TAR=\"%s\")\n", t->pathname);
507 th_print(t);
508#endif
509
510 if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
511 {
512#ifdef DEBUG
513 printf("th_write(): using gnu_longlink (\"%s\")\n",
514 t->th_buf.gnu_longlink);
515#endif
516 /* save old size and type */
517 type2 = t->th_buf.typeflag;
518 sz2 = th_get_size(t);
519
520 /* write out initial header block with fake size and type */
521 t->th_buf.typeflag = GNU_LONGLINK_TYPE;
522 sz = strlen(t->th_buf.gnu_longlink);
523 th_set_size(t, sz);
524 th_finish(t);
525 i = tar_block_write(t, &(t->th_buf));
526 if (i != T_BLOCKSIZE)
527 {
528 if (i != -1)
529 errno = EINVAL;
530 return -1;
531 }
532
533 /* write out extra blocks containing long name */
534 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
535 ptr = t->th_buf.gnu_longlink; j > 1;
536 j--, ptr += T_BLOCKSIZE)
537 {
538 i = tar_block_write(t, ptr);
539 if (i != T_BLOCKSIZE)
540 {
541 if (i != -1)
542 errno = EINVAL;
543 return -1;
544 }
545 }
546 memset(buf, 0, T_BLOCKSIZE);
547 strncpy(buf, ptr, T_BLOCKSIZE);
548 i = tar_block_write(t, &buf);
549 if (i != T_BLOCKSIZE)
550 {
551 if (i != -1)
552 errno = EINVAL;
553 return -1;
554 }
555
556 /* reset type and size to original values */
557 t->th_buf.typeflag = type2;
558 th_set_size(t, sz2);
559 }
560
561 if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
562 {
563#ifdef DEBUG
564 printf("th_write(): using gnu_longname (\"%s\")\n",
565 t->th_buf.gnu_longname);
566#endif
567 /* save old size and type */
568 type2 = t->th_buf.typeflag;
569 sz2 = th_get_size(t);
570
571 /* write out initial header block with fake size and type */
572 t->th_buf.typeflag = GNU_LONGNAME_TYPE;
573 sz = strlen(t->th_buf.gnu_longname);
574 th_set_size(t, sz);
575 th_finish(t);
576 i = tar_block_write(t, &(t->th_buf));
577 if (i != T_BLOCKSIZE)
578 {
579 if (i != -1)
580 errno = EINVAL;
581 return -1;
582 }
583
584 /* write out extra blocks containing long name */
585 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
586 ptr = t->th_buf.gnu_longname; j > 1;
587 j--, ptr += T_BLOCKSIZE)
588 {
589 i = tar_block_write(t, ptr);
590 if (i != T_BLOCKSIZE)
591 {
592 if (i != -1)
593 errno = EINVAL;
594 return -1;
595 }
596 }
597 memset(buf, 0, T_BLOCKSIZE);
598 strncpy(buf, ptr, T_BLOCKSIZE);
599 i = tar_block_write(t, &buf);
600 if (i != T_BLOCKSIZE)
601 {
602 if (i != -1)
603 errno = EINVAL;
604 return -1;
605 }
606
607 /* reset type and size to original values */
608 t->th_buf.typeflag = type2;
609 th_set_size(t, sz2);
610 }
611
Ethan Yonker71187742017-01-13 13:30:10 -0600612 memset(buf, 0, T_BLOCKSIZE);
613 ptr = buf;
Matt Mower87413642017-01-17 21:14:46 -0600614
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200615 if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
616 {
617#ifdef DEBUG
618 printf("th_write(): using selinux_context (\"%s\")\n",
619 t->th_buf.selinux_context);
620#endif
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200621 /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */
622 // size newline
623 sz = SELINUX_TAG_LEN + strlen(t->th_buf.selinux_context) + 3 + 1;
624
625 if(sz >= 100) // another ascci digit for size
626 ++sz;
627
Ethan Yonker71187742017-01-13 13:30:10 -0600628 total_sz += sz;
629 snprintf(ptr, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", (int)sz, t->th_buf.selinux_context);
630 ptr += sz;
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200631 }
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200632
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600633#ifdef HAVE_EXT4_CRYPT
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500634 if((t->options & TAR_STORE_EXT4_POL) && t->th_buf.eep != NULL)
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600635 {
636#ifdef DEBUG
637 printf("th_write(): using e4crypt_policy %s\n",
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500638 t->th_buf.eep->master_key_descriptor);
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600639#endif
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500640 /* 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 -0600641 // size newline
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500642 sz = E4CRYPT_TAG_LEN + sizeof(struct ext4_encryption_policy) + 1 + 3 + 1;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600643
644 if(sz >= 100) // another ascci digit for size
645 ++sz;
646
Ethan Yonker71187742017-01-13 13:30:10 -0600647 if (total_sz + sz >= T_BLOCKSIZE)
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600648 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500649 if (th_write_extended(t, &buf[0], total_sz))
Ethan Yonker71187742017-01-13 13:30:10 -0600650 return -1;
651 ptr = buf;
652 total_sz = sz;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600653 }
Ethan Yonker71187742017-01-13 13:30:10 -0600654 else
655 total_sz += sz;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600656
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500657 snprintf(ptr, T_BLOCKSIZE, "%d "E4CRYPT_TAG"2", (int)sz);
658 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 -0600659 char *nlptr = ptr + sz - 1;
660 *nlptr = '\n';
661 ptr += sz;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600662 }
663#endif
664
bigbiff7ba75002020-04-11 20:47:09 -0400665#ifdef USE_FSCRYPT
666 if((t->options & TAR_STORE_FSCRYPT_POL) && t->th_buf.fep != NULL)
667 {
668#ifdef DEBUG
669 printf("th_write(): using fscrypt_policy %s\n",
670 t->th_buf.fep->master_key_descriptor);
671#endif
672 /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *version code* *content* *newline* */
673 // size newline
674 sz = FSCRYPT_TAG_LEN + sizeof(struct fscrypt_encryption_policy) + 1 + 3 + 1;
675
676 if(sz >= 100) // another ascci digit for size
677 ++sz;
678
679 if (total_sz + sz >= T_BLOCKSIZE)
680 {
681 if (th_write_extended(t, &buf[0], total_sz))
682 return -1;
683 ptr = buf;
684 total_sz = sz;
685 }
686 else
687 total_sz += sz;
688
689 snprintf(ptr, T_BLOCKSIZE, "%d "FSCRYPT_TAG"0", (int)sz);
690 memcpy(ptr + sz - sizeof(struct fscrypt_encryption_policy) - 1, t->th_buf.fep, sizeof(struct fscrypt_encryption_policy));
691 char *nlptr = ptr + sz - 1;
692 *nlptr = '\n';
693 ptr += sz;
694 }
695#endif
696
Ethan Yonker71187742017-01-13 13:30:10 -0600697 if((t->options & TAR_STORE_POSIX_CAP) && t->th_buf.has_cap_data)
698 {
699#ifdef DEBUG
700 printf("th_write(): has a posix capability\n");
701#endif
702 sz = CAPABILITIES_TAG_LEN + sizeof(struct vfs_cap_data) + 3 + 1;
703
704 if(sz >= 100) // another ascci digit for size
705 ++sz;
706
707 if (total_sz + sz >= T_BLOCKSIZE)
708 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500709 if (th_write_extended(t, &buf[0], total_sz))
Ethan Yonker71187742017-01-13 13:30:10 -0600710 return -1;
711 ptr = buf;
712 total_sz = sz;
713 }
714 else
715 total_sz += sz;
716
717 snprintf(ptr, T_BLOCKSIZE, "%d "CAPABILITIES_TAG, (int)sz);
718 memcpy(ptr + CAPABILITIES_TAG_LEN + 3, &t->th_buf.cap_data, sizeof(struct vfs_cap_data));
719 char *nlptr = ptr + sz - 1;
720 *nlptr = '\n';
721 ptr += sz;
722 }
Ethan Yonker8d039f72017-02-03 14:26:15 -0600723 if (t->options & TAR_STORE_ANDROID_USER_XATTR)
724 {
725 if (t->th_buf.has_user_default) {
726#ifdef DEBUG
727 printf("th_write(): has android user.default xattr\n");
728#endif
729 sz = ANDROID_USER_DEFAULT_TAG_LEN + 3 + 1;
730
731 if (total_sz + sz >= T_BLOCKSIZE)
732 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500733 if (th_write_extended(t, &buf[0], total_sz))
Ethan Yonker8d039f72017-02-03 14:26:15 -0600734 return -1;
735 ptr = buf;
736 total_sz = sz;
737 }
738 else
739 total_sz += sz;
740
741 snprintf(ptr, T_BLOCKSIZE, "%d "ANDROID_USER_DEFAULT_TAG, (int)sz);
742 char *nlptr = ptr + sz - 1;
743 *nlptr = '\n';
744 ptr += sz;
745 }
746 if (t->th_buf.has_user_cache) {
747#ifdef DEBUG
748 printf("th_write(): has android user.inode_cache xattr\n");
749#endif
750 sz = ANDROID_USER_CACHE_TAG_LEN + 3 + 1;
751
752 if (total_sz + sz >= T_BLOCKSIZE)
753 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500754 if (th_write_extended(t, &buf[0], total_sz))
Ethan Yonker8d039f72017-02-03 14:26:15 -0600755 return -1;
756 ptr = buf;
757 total_sz = sz;
758 }
759 else
760 total_sz += sz;
761
762 snprintf(ptr, T_BLOCKSIZE, "%d "ANDROID_USER_CACHE_TAG, (int)sz);
763 char *nlptr = ptr + sz - 1;
764 *nlptr = '\n';
765 ptr += sz;
766 }
767 if (t->th_buf.has_user_code_cache) {
768#ifdef DEBUG
769 printf("th_write(): has android user.inode_code_cache xattr\n");
770#endif
771 sz = ANDROID_USER_CODE_CACHE_TAG_LEN + 3 + 1;
772
773 if (total_sz + sz >= T_BLOCKSIZE)
774 {
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500775 if (th_write_extended(t, &buf[0], total_sz))
Ethan Yonker8d039f72017-02-03 14:26:15 -0600776 return -1;
777 ptr = buf;
778 total_sz = sz;
779 }
780 else
781 total_sz += sz;
782
783 snprintf(ptr, T_BLOCKSIZE, "%d "ANDROID_USER_CODE_CACHE_TAG, (int)sz);
784 char *nlptr = ptr + sz - 1;
785 *nlptr = '\n';
786 ptr += sz;
787 }
788 }
Ethan Yonkerfefe5912017-09-30 22:22:13 -0500789 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 -0600790 return -1;
791
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500792 th_finish(t);
793
794#ifdef DEBUG
795 /* print tar header */
796 th_print(t);
797#endif
798
799 i = tar_block_write(t, &(t->th_buf));
800 if (i != T_BLOCKSIZE)
801 {
802 if (i != -1)
803 errno = EINVAL;
804 return -1;
805 }
806
807#ifdef DEBUG
808 puts("th_write(): returning 0");
809#endif
810 return 0;
811}
812
813