blob: d8ba3cad5035de6ebc51a22691395548854ef3ec [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** append.c - libtar code to append files to a tar archive
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>
14
15#include <stdio.h>
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -050016#include <stdlib.h>
17#include <string.h>
bigbiff bigbiff9c754052013-01-09 09:09:08 -050018#include <errno.h>
19#include <fcntl.h>
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -050020#include <time.h>
bigbiff bigbiff9c754052013-01-09 09:09:08 -050021#include <sys/param.h>
22#include <sys/types.h>
Ethan Yonker79f88bd2016-12-09 14:52:12 -060023#include <stdbool.h>
bigbiff bigbiff9c754052013-01-09 09:09:08 -050024
Ethan Yonker71187742017-01-13 13:30:10 -060025#include <sys/capability.h>
26#include <sys/xattr.h>
27#include <linux/xattr.h>
28
bigbiff bigbiff9c754052013-01-09 09:09:08 -050029#ifdef STDC_HEADERS
30# include <stdlib.h>
31# include <string.h>
32#endif
33
34#ifdef HAVE_UNISTD_H
35# include <unistd.h>
36#endif
37
Matt Mower87413642017-01-17 21:14:46 -060038#include <selinux/selinux.h>
bigbiff bigbiff9c754052013-01-09 09:09:08 -050039
Ethan Yonker79f88bd2016-12-09 14:52:12 -060040#ifdef HAVE_EXT4_CRYPT
41# include "ext4crypt_tar.h"
42#endif
43
bigbiff bigbiff9c754052013-01-09 09:09:08 -050044struct tar_dev
45{
46 dev_t td_dev;
47 libtar_hash_t *td_h;
48};
49typedef struct tar_dev tar_dev_t;
50
51struct tar_ino
52{
53 ino_t ti_ino;
54 char ti_name[MAXPATHLEN];
55};
56typedef struct tar_ino tar_ino_t;
57
58
59/* free memory associated with a tar_dev_t */
60void
61tar_dev_free(tar_dev_t *tdp)
62{
63 libtar_hash_free(tdp->td_h, free);
64 free(tdp);
65}
66
67
68/* appends a file to the tar archive */
69int
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -050070tar_append_file(TAR *t, const char *realname, const char *savename)
bigbiff bigbiff9c754052013-01-09 09:09:08 -050071{
72 struct stat s;
73 int i;
74 libtar_hashptr_t hp;
75 tar_dev_t *td = NULL;
76 tar_ino_t *ti = NULL;
77 char path[MAXPATHLEN];
78
79#ifdef DEBUG
80 printf("==> tar_append_file(TAR=0x%lx (\"%s\"), realname=\"%s\", "
81 "savename=\"%s\")\n", t, t->pathname, realname,
82 (savename ? savename : "[NULL]"));
83#endif
84
85 if (lstat(realname, &s) != 0)
86 {
87#ifdef DEBUG
88 perror("lstat()");
89#endif
90 return -1;
91 }
92
93 /* set header block */
94#ifdef DEBUG
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -050095 puts("tar_append_file(): setting header block...");
bigbiff bigbiff9c754052013-01-09 09:09:08 -050096#endif
97 memset(&(t->th_buf), 0, sizeof(struct tar_header));
98 th_set_from_stat(t, &s);
99
100 /* set the header path */
101#ifdef DEBUG
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500102 puts("tar_append_file(): setting header path...");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500103#endif
104 th_set_path(t, (savename ? savename : realname));
105
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200106 /* get selinux context */
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500107 if (t->options & TAR_STORE_SELINUX)
108 {
109 if (t->th_buf.selinux_context != NULL)
110 {
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200111 free(t->th_buf.selinux_context);
112 t->th_buf.selinux_context = NULL;
113 }
114
115 security_context_t selinux_context = NULL;
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500116 if (lgetfilecon(realname, &selinux_context) >= 0)
117 {
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200118 t->th_buf.selinux_context = strdup(selinux_context);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500119 printf(" ==> set selinux context: %s\n", selinux_context);
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200120 freecon(selinux_context);
121 }
122 else
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500123 {
124#ifdef DEBUG
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200125 perror("Failed to get selinux context");
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500126#endif
127 }
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200128 }
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500129
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600130#ifdef HAVE_EXT4_CRYPT
131 if (TH_ISDIR(t) && t->options & TAR_STORE_EXT4_POL)
132 {
133 if (t->th_buf.e4crypt_policy != NULL)
134 {
135 free(t->th_buf.e4crypt_policy);
136 t->th_buf.e4crypt_policy = NULL;
137 }
138
139 char e4crypt_policy[EXT4_KEY_DESCRIPTOR_SIZE];
140 if (e4crypt_policy_get(realname, e4crypt_policy, EXT4_KEY_DESCRIPTOR_SIZE, 0))
141 {
142 char tar_policy[EXT4_KEY_DESCRIPTOR_SIZE];
143 memset(tar_policy, 0, sizeof(tar_policy));
144 char policy_hex[EXT4_KEY_DESCRIPTOR_HEX];
145 policy_to_hex(e4crypt_policy, policy_hex);
146 if (lookup_ref_key(e4crypt_policy, &tar_policy)) {
147 printf("found policy '%s' - '%s' - '%s'\n", realname, tar_policy, policy_hex);
148 t->th_buf.e4crypt_policy = strdup(tar_policy);
149 } else {
150 printf("failed to lookup tar policy for '%s' - '%s'\n", realname, policy_hex);
151 return -1;
152 }
153 } // else no policy found, but this is not an error as not all dirs will have a policy
154 }
155#endif
156
Ethan Yonker71187742017-01-13 13:30:10 -0600157 /* get posix file capabilities */
158 if (TH_ISREG(t) && t->options & TAR_STORE_POSIX_CAP)
159 {
160 if (t->th_buf.has_cap_data)
161 {
162 memset(&t->th_buf.cap_data, 0, sizeof(struct vfs_cap_data));
163 t->th_buf.has_cap_data = 0;
164 }
165
166 if (getxattr(realname, XATTR_NAME_CAPS, &t->th_buf.cap_data, sizeof(struct vfs_cap_data)) >= 0)
167 {
168 t->th_buf.has_cap_data = 1;
169#if 1 //def DEBUG
170 print_caps(&t->th_buf.cap_data);
171#endif
172 }
173 }
174
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500175 /* check if it's a hardlink */
176#ifdef DEBUG
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500177 puts("tar_append_file(): checking inode cache for hardlink...");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500178#endif
179 libtar_hashptr_reset(&hp);
180 if (libtar_hash_getkey(t->h, &hp, &(s.st_dev),
181 (libtar_matchfunc_t)dev_match) != 0)
182 td = (tar_dev_t *)libtar_hashptr_data(&hp);
183 else
184 {
185#ifdef DEBUG
186 printf("+++ adding hash for device (0x%lx, 0x%lx)...\n",
187 major(s.st_dev), minor(s.st_dev));
188#endif
189 td = (tar_dev_t *)calloc(1, sizeof(tar_dev_t));
190 td->td_dev = s.st_dev;
191 td->td_h = libtar_hash_new(256, (libtar_hashfunc_t)ino_hash);
192 if (td->td_h == NULL)
193 return -1;
194 if (libtar_hash_add(t->h, td) == -1)
195 return -1;
196 }
197 libtar_hashptr_reset(&hp);
198 if (libtar_hash_getkey(td->td_h, &hp, &(s.st_ino),
199 (libtar_matchfunc_t)ino_match) != 0)
200 {
201 ti = (tar_ino_t *)libtar_hashptr_data(&hp);
202#ifdef DEBUG
203 printf(" tar_append_file(): encoding hard link \"%s\" "
204 "to \"%s\"...\n", realname, ti->ti_name);
205#endif
206 t->th_buf.typeflag = LNKTYPE;
207 th_set_link(t, ti->ti_name);
208 }
209 else
210 {
211#ifdef DEBUG
212 printf("+++ adding entry: device (0x%lx,0x%lx), inode %ld "
213 "(\"%s\")...\n", major(s.st_dev), minor(s.st_dev),
214 s.st_ino, realname);
215#endif
216 ti = (tar_ino_t *)calloc(1, sizeof(tar_ino_t));
217 if (ti == NULL)
218 return -1;
219 ti->ti_ino = s.st_ino;
220 snprintf(ti->ti_name, sizeof(ti->ti_name), "%s",
221 savename ? savename : realname);
222 libtar_hash_add(td->td_h, ti);
223 }
224
225 /* check if it's a symlink */
226 if (TH_ISSYM(t))
227 {
228 i = readlink(realname, path, sizeof(path));
229 if (i == -1)
230 return -1;
231 if (i >= MAXPATHLEN)
232 i = MAXPATHLEN - 1;
233 path[i] = '\0';
234#ifdef DEBUG
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500235 printf("tar_append_file(): encoding symlink \"%s\" -> "
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500236 "\"%s\"...\n", realname, path);
237#endif
238 th_set_link(t, path);
239 }
240
241 /* print file info */
242 if (t->options & TAR_VERBOSE)
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500243 printf("%s\n", th_get_pathname(t));
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500244
245#ifdef DEBUG
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500246 puts("tar_append_file(): writing header");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500247#endif
248 /* write header */
249 if (th_write(t) != 0)
250 {
251#ifdef DEBUG
252 printf("t->fd = %d\n", t->fd);
253#endif
254 return -1;
255 }
256#ifdef DEBUG
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500257 puts("tar_append_file(): back from th_write()");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500258#endif
259
260 /* if it's a regular file, write the contents as well */
261 if (TH_ISREG(t) && tar_append_regfile(t, realname) != 0)
262 return -1;
263
264 return 0;
265}
266
267
268/* write EOF indicator */
269int
270tar_append_eof(TAR *t)
271{
272 int i, j;
273 char block[T_BLOCKSIZE];
274
275 memset(&block, 0, T_BLOCKSIZE);
276 for (j = 0; j < 2; j++)
277 {
278 i = tar_block_write(t, &block);
279 if (i != T_BLOCKSIZE)
280 {
281 if (i != -1)
282 errno = EINVAL;
283 return -1;
284 }
285 }
286
287 return 0;
288}
289
290
291/* add file contents to a tarchive */
292int
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500293tar_append_regfile(TAR *t, const char *realname)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500294{
295 char block[T_BLOCKSIZE];
296 int filefd;
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500297 int64_t i, size;
298 ssize_t j;
299 int rv = -1;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500300
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500301#if defined(O_BINARY)
302 filefd = open(realname, O_RDONLY|O_BINARY);
303#else
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500304 filefd = open(realname, O_RDONLY);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500305#endif
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500306 if (filefd == -1)
307 {
308#ifdef DEBUG
309 perror("open()");
310#endif
311 return -1;
312 }
313
314 size = th_get_size(t);
315 for (i = size; i > T_BLOCKSIZE; i -= T_BLOCKSIZE)
316 {
317 j = read(filefd, &block, T_BLOCKSIZE);
318 if (j != T_BLOCKSIZE)
319 {
320 if (j != -1)
321 errno = EINVAL;
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500322 goto fail;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500323 }
324 if (tar_block_write(t, &block) == -1)
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500325 goto fail;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500326 }
327
328 if (i > 0)
329 {
330 j = read(filefd, &block, i);
331 if (j == -1)
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500332 goto fail;
333 memset(&(block[i]), 0, T_BLOCKSIZE - i);
334 if (tar_block_write(t, &block) == -1)
335 goto fail;
336 }
337
338 /* success! */
339 rv = 0;
340fail:
341 close(filefd);
342
343 return rv;
344}
345
346
347/* add file contents to a tarchive */
348int
349tar_append_file_contents(TAR *t, const char *savename, mode_t mode,
350 uid_t uid, gid_t gid, void *buf, size_t len)
351{
352 struct stat st;
353
354 memset(&st, 0, sizeof(st));
355 st.st_mode = S_IFREG | mode;
356 st.st_uid = uid;
357 st.st_gid = gid;
358 st.st_mtime = time(NULL);
359 st.st_size = len;
360
361 th_set_from_stat(t, &st);
362 th_set_path(t, savename);
363
364 /* write header */
365 if (th_write(t) != 0)
366 {
367#ifdef DEBUG
368 fprintf(stderr, "tar_append_file_contents(): could not write header, t->fd = %d\n", t->fd);
369#endif
370 return -1;
371 }
372
373 return tar_append_buffer(t, buf, len);
374}
375
376int
377tar_append_buffer(TAR *t, void *buf, size_t len)
378{
379 char block[T_BLOCKSIZE];
380 int filefd;
381 int i, j;
382 size_t size = len;
383
384 for (i = size; i > T_BLOCKSIZE; i -= T_BLOCKSIZE)
385 {
386 if (tar_block_write(t, buf) == -1)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500387 return -1;
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500388 buf = (char *)buf + T_BLOCKSIZE;
389 }
390
391 if (i > 0)
392 {
393 memcpy(block, buf, i);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500394 memset(&(block[i]), 0, T_BLOCKSIZE - i);
395 if (tar_block_write(t, &block) == -1)
396 return -1;
397 }
398
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500399 return 0;
400}
401