blob: 69e08bd5489b9f30535bd77711dcb6ae561f1295 [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** extract.c - libtar code to extract a file from 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>
16#include <sys/param.h>
17#include <sys/types.h>
18#include <fcntl.h>
19#include <errno.h>
20#include <utime.h>
Ethan Yonkerc798c9c2015-10-09 11:15:26 -050021#include <string.h>
bigbiff bigbiff9c754052013-01-09 09:09:08 -050022
23#define DEBUG
24#ifdef STDC_HEADERS
25# include <stdlib.h>
26#endif
27
28#ifdef HAVE_UNISTD_H
29# include <unistd.h>
30#endif
31
32#define DEBUG
33
34static int
35tar_set_file_perms(TAR *t, char *realname)
36{
37 mode_t mode;
38 uid_t uid;
39 gid_t gid;
40 struct utimbuf ut;
41 char *filename;
42
43 filename = (realname ? realname : th_get_pathname(t));
44 mode = th_get_mode(t);
45 uid = th_get_uid(t);
46 gid = th_get_gid(t);
47 ut.modtime = ut.actime = th_get_mtime(t);
48
Dees_Troy71796592013-03-14 20:16:29 +000049#ifdef DEBUG
50 printf(" ==> setting perms: %s (mode %04o, uid %d, gid %d)\n",
51 filename, mode, uid, gid);
52#endif
53
bigbiff bigbiff9c754052013-01-09 09:09:08 -050054 /* change owner/group */
55 if (geteuid() == 0)
56#ifdef HAVE_LCHOWN
57 if (lchown(filename, uid, gid) == -1)
58 {
59# ifdef DEBUG
60 fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
61 filename, uid, gid, strerror(errno));
62# endif
63#else /* ! HAVE_LCHOWN */
64 if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
65 {
66# ifdef DEBUG
67 fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
68 filename, uid, gid, strerror(errno));
69# endif
70#endif /* HAVE_LCHOWN */
71 return -1;
72 }
73
74 /* change access/modification time */
75 if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
76 {
77#ifdef DEBUG
78 perror("utime()");
79#endif
80 return -1;
81 }
82
83 /* change permissions */
84 if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
85 {
86#ifdef DEBUG
87 perror("chmod()");
88#endif
89 return -1;
90 }
91
92 return 0;
93}
94
95
96/* switchboard */
97int
Ethan Yonker1b7a31b2014-07-03 15:09:22 -050098tar_extract_file(TAR *t, char *realname, char *prefix, const int *progress_fd)
bigbiff bigbiff9c754052013-01-09 09:09:08 -050099{
100 int i;
101 char *lnp;
102 int pathname_len;
103 int realname_len;
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500104
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500105 if (t->options & TAR_NOOVERWRITE)
106 {
107 struct stat s;
108
109 if (lstat(realname, &s) == 0 || errno != ENOENT)
110 {
111 errno = EEXIST;
112 return -1;
113 }
114 }
115
116 if (TH_ISDIR(t))
117 {
Dees_Troy71796592013-03-14 20:16:29 +0000118 printf("dir\n");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500119 i = tar_extract_dir(t, realname);
120 if (i == 1)
121 i = 0;
122 }
123 else if (TH_ISLNK(t)) {
124 printf("link\n");
Dees_Troyee6632c2013-02-27 18:07:32 +0000125 i = tar_extract_hardlink(t, realname, prefix);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500126 }
127 else if (TH_ISSYM(t)) {
128 printf("sym\n");
129 i = tar_extract_symlink(t, realname);
130 }
131 else if (TH_ISCHR(t)) {
132 printf("chr\n");
133 i = tar_extract_chardev(t, realname);
134 }
135 else if (TH_ISBLK(t)) {
136 printf("blk\n");
137 i = tar_extract_blockdev(t, realname);
138 }
139 else if (TH_ISFIFO(t)) {
140 printf("fifo\n");
141 i = tar_extract_fifo(t, realname);
142 }
143 else /* if (TH_ISREG(t)) */ {
144 printf("reg\n");
Ethan Yonker1b7a31b2014-07-03 15:09:22 -0500145 i = tar_extract_regfile(t, realname, progress_fd);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500146 }
147
148 if (i != 0) {
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500149 printf("FAILED RESTORE OF FILE i: %s\n", realname);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500150 return i;
151 }
152
153 i = tar_set_file_perms(t, realname);
154 if (i != 0) {
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500155 printf("FAILED SETTING PERMS: %d\n", i);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500156 return i;
157 }
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200158
159#ifdef HAVE_SELINUX
160 if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
161 {
162#ifdef DEBUG
bigbiff bigbiff6b600f92014-01-05 18:13:43 -0500163 printf(" Restoring SELinux context %s to file %s\n", t->th_buf.selinux_context, realname);
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200164#endif
bigbiff bigbiffc49d7062013-10-11 20:28:00 -0400165 if (lsetfilecon(realname, t->th_buf.selinux_context) < 0) {
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200166 fprintf(stderr, "Failed to restore SELinux context %s!\n", strerror(errno));
bigbiff bigbiffc49d7062013-10-11 20:28:00 -0400167 }
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200168 }
169#endif
170
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500171/*
172 pathname_len = strlen(th_get_pathname(t)) + 1;
173 realname_len = strlen(realname) + 1;
174 lnp = (char *)calloc(1, pathname_len + realname_len);
175 if (lnp == NULL)
176 return -1;
177 strcpy(&lnp[0], th_get_pathname(t));
178 strcpy(&lnp[pathname_len], realname);
179#ifdef DEBUG
180 printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
181 "value=\"%s\"\n", th_get_pathname(t), realname);
182#endif
183 if (libtar_hash_add(t->h, lnp) != 0)
184 return -1;
185 free(lnp);
186*/
187 return 0;
188}
189
190
191/* extract regular file */
192int
Ethan Yonker1b7a31b2014-07-03 15:09:22 -0500193tar_extract_regfile(TAR *t, char *realname, const int *progress_fd)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500194{
Dees_Troy71796592013-03-14 20:16:29 +0000195 //mode_t mode;
Vojtech Bocek78ab0c52015-03-20 15:34:45 +0100196 size_t size, i;
Dees_Troy71796592013-03-14 20:16:29 +0000197 //uid_t uid;
198 //gid_t gid;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500199 int fdout;
Vojtech Bocek78ab0c52015-03-20 15:34:45 +0100200 int k;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500201 char buf[T_BLOCKSIZE];
202 char *filename;
203
204 fflush(NULL);
205#ifdef DEBUG
206 printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
207 realname);
208#endif
209
210 if (!TH_ISREG(t))
211 {
212 errno = EINVAL;
213 return -1;
214 }
215
216 filename = (realname ? realname : th_get_pathname(t));
Dees_Troy71796592013-03-14 20:16:29 +0000217 //mode = th_get_mode(t);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500218 size = th_get_size(t);
Dees_Troy71796592013-03-14 20:16:29 +0000219 //uid = th_get_uid(t);
220 //gid = th_get_gid(t);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500221
222 if (mkdirhier(dirname(filename)) == -1)
223 return -1;
224
225#ifdef DEBUG
Dees_Troy71796592013-03-14 20:16:29 +0000226 //printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
227 // filename, mode, uid, gid, size);
228 printf(" ==> extracting: %s (file size %d bytes)\n",
229 filename, size);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500230#endif
231 fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
232#ifdef O_BINARY
233 | O_BINARY
234#endif
235 , 0666);
236 if (fdout == -1)
237 {
238#ifdef DEBUG
239 perror("open()");
240#endif
241 return -1;
242 }
243
244#if 0
245 /* change the owner. (will only work if run as root) */
246 if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
247 {
248#ifdef DEBUG
249 perror("fchown()");
250#endif
251 return -1;
252 }
253
254 /* make sure the mode isn't inheritted from a file we're overwriting */
255 if (fchmod(fdout, mode & 07777) == -1)
256 {
257#ifdef DEBUG
258 perror("fchmod()");
259#endif
260 return -1;
261 }
262#endif
263
264 /* extract the file */
Vojtech Bocek78ab0c52015-03-20 15:34:45 +0100265 for (i = size; i > 0; i -= tar_min(i, T_BLOCKSIZE))
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500266 {
267 k = tar_block_read(t, buf);
268 if (k != T_BLOCKSIZE)
269 {
270 if (k != -1)
271 errno = EINVAL;
272 return -1;
273 }
274
275 /* write block to output file */
276 if (write(fdout, buf,
277 ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
278 return -1;
279 }
280
281 /* close output file */
282 if (close(fdout) == -1)
283 return -1;
284
285#ifdef DEBUG
286 printf("### done extracting %s\n", filename);
287#endif
288
Ethan Yonker1b7a31b2014-07-03 15:09:22 -0500289 if (*progress_fd != 0) {
290 unsigned long long file_size = (unsigned long long)(size);
291 write(*progress_fd, &file_size, sizeof(file_size));
292 }
293
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500294 return 0;
295}
296
297
298/* skip regfile */
299int
300tar_skip_regfile(TAR *t)
301{
Vojtech Bocek78ab0c52015-03-20 15:34:45 +0100302 int k;
303 size_t size, i;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500304 char buf[T_BLOCKSIZE];
305
306 if (!TH_ISREG(t))
307 {
308 errno = EINVAL;
309 return -1;
310 }
311
312 size = th_get_size(t);
Vojtech Bocek78ab0c52015-03-20 15:34:45 +0100313 for (i = size; i > 0; i -= tar_min(i, T_BLOCKSIZE))
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500314 {
315 k = tar_block_read(t, buf);
316 if (k != T_BLOCKSIZE)
317 {
318 if (k != -1)
319 errno = EINVAL;
320 return -1;
321 }
322 }
323
324 return 0;
325}
326
327
328/* hardlink */
329int
Dees_Troyee6632c2013-02-27 18:07:32 +0000330tar_extract_hardlink(TAR * t, char *realname, char *prefix)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500331{
332 char *filename;
333 char *linktgt = NULL;
334 char *lnp;
335 libtar_hashptr_t hp;
336
337 if (!TH_ISLNK(t))
338 {
339 errno = EINVAL;
340 return -1;
341 }
342
343 filename = (realname ? realname : th_get_pathname(t));
344 if (mkdirhier(dirname(filename)) == -1)
345 return -1;
346 libtar_hashptr_reset(&hp);
347 if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
348 (libtar_matchfunc_t)libtar_str_match) != 0)
349 {
350 lnp = (char *)libtar_hashptr_data(&hp);
351 linktgt = &lnp[strlen(lnp) + 1];
352 }
353 else
354 linktgt = th_get_linkname(t);
Dees_Troyee6632c2013-02-27 18:07:32 +0000355 char *newtgt = strdup(linktgt);
356 sprintf(linktgt, "%s/%s", prefix, newtgt);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500357#ifdef DEBUG
358 printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
359#endif
360 if (link(linktgt, filename) == -1)
361 {
362#ifdef DEBUG
363 perror("link()");
364#endif
Dees_Troyf96fb972013-03-01 22:34:25 +0000365 printf("Failed restore of hardlink '%s' but returning as if nothing bad happened anyway\n", filename);
366 return 0; // Used to be -1
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500367 }
368
369 return 0;
370}
371
372
373/* symlink */
374int
375tar_extract_symlink(TAR *t, char *realname)
376{
377 char *filename;
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500378
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500379 if (!TH_ISSYM(t))
380 {
381 printf("not a sym\n");
382 errno = EINVAL;
383 return -1;
384 }
385
386 filename = (realname ? realname : th_get_pathname(t));
387 printf("file: %s\n", filename);
388 if (mkdirhier(dirname(filename)) == -1) {
389 printf("mkdirhier\n");
390 return -1;
391 }
392
393 if (unlink(filename) == -1 && errno != ENOENT) {
394 printf("unlink\n");
395 return -1;
396 }
397
398#ifdef DEBUG
399 printf(" ==> extracting: %s (symlink to %s)\n",
400 filename, th_get_linkname(t));
401#endif
402 if (symlink(th_get_linkname(t), filename) == -1)
403 {
404#ifdef DEBUG
405 perror("symlink()");
406#endif
407 return -1;
408 }
409
410 return 0;
411}
412
413
414/* character device */
415int
416tar_extract_chardev(TAR *t, char *realname)
417{
418 mode_t mode;
419 unsigned long devmaj, devmin;
420 char *filename;
421
422 if (!TH_ISCHR(t))
423 {
424 errno = EINVAL;
425 return -1;
426 }
427
428 filename = (realname ? realname : th_get_pathname(t));
429 mode = th_get_mode(t);
430 devmaj = th_get_devmajor(t);
431 devmin = th_get_devminor(t);
432
433 if (mkdirhier(dirname(filename)) == -1)
434 return -1;
435
436#ifdef DEBUG
437 printf(" ==> extracting: %s (character device %ld,%ld)\n",
438 filename, devmaj, devmin);
439#endif
440 if (mknod(filename, mode | S_IFCHR,
441 compat_makedev(devmaj, devmin)) == -1)
442 {
443#ifdef DEBUG
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500444 printf("mknod() failed, returning good anyway");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500445#endif
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500446 return 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500447 }
448
449 return 0;
450}
451
452
453/* block device */
454int
455tar_extract_blockdev(TAR *t, char *realname)
456{
457 mode_t mode;
458 unsigned long devmaj, devmin;
459 char *filename;
460
461 if (!TH_ISBLK(t))
462 {
463 errno = EINVAL;
464 return -1;
465 }
466
467 filename = (realname ? realname : th_get_pathname(t));
468 mode = th_get_mode(t);
469 devmaj = th_get_devmajor(t);
470 devmin = th_get_devminor(t);
471
472 if (mkdirhier(dirname(filename)) == -1)
473 return -1;
474
475#ifdef DEBUG
476 printf(" ==> extracting: %s (block device %ld,%ld)\n",
477 filename, devmaj, devmin);
478#endif
479 if (mknod(filename, mode | S_IFBLK,
480 compat_makedev(devmaj, devmin)) == -1)
481 {
482#ifdef DEBUG
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500483 printf("mknod() failed but returning anyway");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500484#endif
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500485 return 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500486 }
487
488 return 0;
489}
490
491
492/* directory */
493int
494tar_extract_dir(TAR *t, char *realname)
495{
496 mode_t mode;
497 char *filename;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500498 if (!TH_ISDIR(t))
499 {
500 errno = EINVAL;
501 return -1;
502 }
503
504 filename = (realname ? realname : th_get_pathname(t));
505 mode = th_get_mode(t);
506
Dees_Troy71796592013-03-14 20:16:29 +0000507 if (mkdirhier(dirname(filename)) == -1) {
508 printf("tar_extract_dir mkdirhier failed\n");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500509 return -1;
Dees_Troy71796592013-03-14 20:16:29 +0000510 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500511
512#ifdef DEBUG
513 printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
514 mode);
515#endif
516 if (mkdir(filename, mode) == -1)
517 {
518 if (errno == EEXIST)
519 {
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500520#ifdef DEBUG
Dees_Troy71796592013-03-14 20:16:29 +0000521 printf(" *** using existing directory");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500522#endif
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500523 }
524 else
525 {
526#ifdef DEBUG
527 perror("mkdir()");
528#endif
529 return -1;
530 }
531 }
532
533 return 0;
534}
535
536
537/* FIFO */
538int
539tar_extract_fifo(TAR *t, char *realname)
540{
541 mode_t mode;
542 char *filename;
543
544 if (!TH_ISFIFO(t))
545 {
546 errno = EINVAL;
547 return -1;
548 }
549
550 filename = (realname ? realname : th_get_pathname(t));
551 mode = th_get_mode(t);
552
553 if (mkdirhier(dirname(filename)) == -1)
554 return -1;
555
556#ifdef DEBUG
557 printf(" ==> extracting: %s (fifo)\n", filename);
558#endif
559 if (mkfifo(filename, mode) == -1)
560 {
561#ifdef DEBUG
562 perror("mkfifo()");
563#endif
564 return -1;
565 }
566
567 return 0;
568}
569
570