blob: e605aca430f582b122bdedcc08d496bc6042eafc [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>
21
22#define DEBUG
23#ifdef STDC_HEADERS
24# include <stdlib.h>
25#endif
26
27#ifdef HAVE_UNISTD_H
28# include <unistd.h>
29#endif
30
31#define DEBUG
32
33static int
34tar_set_file_perms(TAR *t, char *realname)
35{
36 mode_t mode;
37 uid_t uid;
38 gid_t gid;
39 struct utimbuf ut;
40 char *filename;
41
42 filename = (realname ? realname : th_get_pathname(t));
43 mode = th_get_mode(t);
44 uid = th_get_uid(t);
45 gid = th_get_gid(t);
46 ut.modtime = ut.actime = th_get_mtime(t);
47
Dees_Troy71796592013-03-14 20:16:29 +000048#ifdef DEBUG
49 printf(" ==> setting perms: %s (mode %04o, uid %d, gid %d)\n",
50 filename, mode, uid, gid);
51#endif
52
bigbiff bigbiff9c754052013-01-09 09:09:08 -050053 /* change owner/group */
54 if (geteuid() == 0)
55#ifdef HAVE_LCHOWN
56 if (lchown(filename, uid, gid) == -1)
57 {
58# ifdef DEBUG
59 fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
60 filename, uid, gid, strerror(errno));
61# endif
62#else /* ! HAVE_LCHOWN */
63 if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
64 {
65# ifdef DEBUG
66 fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
67 filename, uid, gid, strerror(errno));
68# endif
69#endif /* HAVE_LCHOWN */
70 return -1;
71 }
72
73 /* change access/modification time */
74 if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
75 {
76#ifdef DEBUG
77 perror("utime()");
78#endif
79 return -1;
80 }
81
82 /* change permissions */
83 if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
84 {
85#ifdef DEBUG
86 perror("chmod()");
87#endif
88 return -1;
89 }
90
91 return 0;
92}
93
94
95/* switchboard */
96int
Ethan Yonker1b7a31b2014-07-03 15:09:22 -050097tar_extract_file(TAR *t, char *realname, char *prefix, const int *progress_fd)
bigbiff bigbiff9c754052013-01-09 09:09:08 -050098{
99 int i;
100 char *lnp;
101 int pathname_len;
102 int realname_len;
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500103
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500104 if (t->options & TAR_NOOVERWRITE)
105 {
106 struct stat s;
107
108 if (lstat(realname, &s) == 0 || errno != ENOENT)
109 {
110 errno = EEXIST;
111 return -1;
112 }
113 }
114
115 if (TH_ISDIR(t))
116 {
Dees_Troy71796592013-03-14 20:16:29 +0000117 printf("dir\n");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500118 i = tar_extract_dir(t, realname);
119 if (i == 1)
120 i = 0;
121 }
122 else if (TH_ISLNK(t)) {
123 printf("link\n");
Dees_Troyee6632c2013-02-27 18:07:32 +0000124 i = tar_extract_hardlink(t, realname, prefix);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500125 }
126 else if (TH_ISSYM(t)) {
127 printf("sym\n");
128 i = tar_extract_symlink(t, realname);
129 }
130 else if (TH_ISCHR(t)) {
131 printf("chr\n");
132 i = tar_extract_chardev(t, realname);
133 }
134 else if (TH_ISBLK(t)) {
135 printf("blk\n");
136 i = tar_extract_blockdev(t, realname);
137 }
138 else if (TH_ISFIFO(t)) {
139 printf("fifo\n");
140 i = tar_extract_fifo(t, realname);
141 }
142 else /* if (TH_ISREG(t)) */ {
143 printf("reg\n");
Ethan Yonker1b7a31b2014-07-03 15:09:22 -0500144 i = tar_extract_regfile(t, realname, progress_fd);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500145 }
146
147 if (i != 0) {
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500148 printf("FAILED RESTORE OF FILE i: %s\n", realname);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500149 return i;
150 }
151
152 i = tar_set_file_perms(t, realname);
153 if (i != 0) {
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500154 printf("FAILED SETTING PERMS: %d\n", i);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500155 return i;
156 }
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200157
158#ifdef HAVE_SELINUX
159 if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
160 {
161#ifdef DEBUG
bigbiff bigbiff6b600f92014-01-05 18:13:43 -0500162 printf(" Restoring SELinux context %s to file %s\n", t->th_buf.selinux_context, realname);
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200163#endif
bigbiff bigbiffc49d7062013-10-11 20:28:00 -0400164 if (lsetfilecon(realname, t->th_buf.selinux_context) < 0) {
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200165 fprintf(stderr, "Failed to restore SELinux context %s!\n", strerror(errno));
bigbiff bigbiffc49d7062013-10-11 20:28:00 -0400166 }
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200167 }
168#endif
169
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500170/*
171 pathname_len = strlen(th_get_pathname(t)) + 1;
172 realname_len = strlen(realname) + 1;
173 lnp = (char *)calloc(1, pathname_len + realname_len);
174 if (lnp == NULL)
175 return -1;
176 strcpy(&lnp[0], th_get_pathname(t));
177 strcpy(&lnp[pathname_len], realname);
178#ifdef DEBUG
179 printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
180 "value=\"%s\"\n", th_get_pathname(t), realname);
181#endif
182 if (libtar_hash_add(t->h, lnp) != 0)
183 return -1;
184 free(lnp);
185*/
186 return 0;
187}
188
189
190/* extract regular file */
191int
Ethan Yonker1b7a31b2014-07-03 15:09:22 -0500192tar_extract_regfile(TAR *t, char *realname, const int *progress_fd)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500193{
Dees_Troy71796592013-03-14 20:16:29 +0000194 //mode_t mode;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500195 size_t size;
Dees_Troy71796592013-03-14 20:16:29 +0000196 //uid_t uid;
197 //gid_t gid;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500198 int fdout;
199 int i, k;
200 char buf[T_BLOCKSIZE];
201 char *filename;
202
203 fflush(NULL);
204#ifdef DEBUG
205 printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
206 realname);
207#endif
208
209 if (!TH_ISREG(t))
210 {
211 errno = EINVAL;
212 return -1;
213 }
214
215 filename = (realname ? realname : th_get_pathname(t));
Dees_Troy71796592013-03-14 20:16:29 +0000216 //mode = th_get_mode(t);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500217 size = th_get_size(t);
Dees_Troy71796592013-03-14 20:16:29 +0000218 //uid = th_get_uid(t);
219 //gid = th_get_gid(t);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500220
221 if (mkdirhier(dirname(filename)) == -1)
222 return -1;
223
224#ifdef DEBUG
Dees_Troy71796592013-03-14 20:16:29 +0000225 //printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
226 // filename, mode, uid, gid, size);
227 printf(" ==> extracting: %s (file size %d bytes)\n",
228 filename, size);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500229#endif
230 fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
231#ifdef O_BINARY
232 | O_BINARY
233#endif
234 , 0666);
235 if (fdout == -1)
236 {
237#ifdef DEBUG
238 perror("open()");
239#endif
240 return -1;
241 }
242
243#if 0
244 /* change the owner. (will only work if run as root) */
245 if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
246 {
247#ifdef DEBUG
248 perror("fchown()");
249#endif
250 return -1;
251 }
252
253 /* make sure the mode isn't inheritted from a file we're overwriting */
254 if (fchmod(fdout, mode & 07777) == -1)
255 {
256#ifdef DEBUG
257 perror("fchmod()");
258#endif
259 return -1;
260 }
261#endif
262
263 /* extract the file */
264 for (i = size; i > 0; i -= T_BLOCKSIZE)
265 {
266 k = tar_block_read(t, buf);
267 if (k != T_BLOCKSIZE)
268 {
269 if (k != -1)
270 errno = EINVAL;
271 return -1;
272 }
273
274 /* write block to output file */
275 if (write(fdout, buf,
276 ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
277 return -1;
278 }
279
280 /* close output file */
281 if (close(fdout) == -1)
282 return -1;
283
284#ifdef DEBUG
285 printf("### done extracting %s\n", filename);
286#endif
287
Ethan Yonker1b7a31b2014-07-03 15:09:22 -0500288 if (*progress_fd != 0) {
289 unsigned long long file_size = (unsigned long long)(size);
290 write(*progress_fd, &file_size, sizeof(file_size));
291 }
292
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500293 return 0;
294}
295
296
297/* skip regfile */
298int
299tar_skip_regfile(TAR *t)
300{
301 int i, k;
302 size_t size;
303 char buf[T_BLOCKSIZE];
304
305 if (!TH_ISREG(t))
306 {
307 errno = EINVAL;
308 return -1;
309 }
310
311 size = th_get_size(t);
312 for (i = size; i > 0; i -= T_BLOCKSIZE)
313 {
314 k = tar_block_read(t, buf);
315 if (k != T_BLOCKSIZE)
316 {
317 if (k != -1)
318 errno = EINVAL;
319 return -1;
320 }
321 }
322
323 return 0;
324}
325
326
327/* hardlink */
328int
Dees_Troyee6632c2013-02-27 18:07:32 +0000329tar_extract_hardlink(TAR * t, char *realname, char *prefix)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500330{
331 char *filename;
332 char *linktgt = NULL;
333 char *lnp;
334 libtar_hashptr_t hp;
335
336 if (!TH_ISLNK(t))
337 {
338 errno = EINVAL;
339 return -1;
340 }
341
342 filename = (realname ? realname : th_get_pathname(t));
343 if (mkdirhier(dirname(filename)) == -1)
344 return -1;
345 libtar_hashptr_reset(&hp);
346 if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
347 (libtar_matchfunc_t)libtar_str_match) != 0)
348 {
349 lnp = (char *)libtar_hashptr_data(&hp);
350 linktgt = &lnp[strlen(lnp) + 1];
351 }
352 else
353 linktgt = th_get_linkname(t);
Dees_Troyee6632c2013-02-27 18:07:32 +0000354 char *newtgt = strdup(linktgt);
355 sprintf(linktgt, "%s/%s", prefix, newtgt);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500356#ifdef DEBUG
357 printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
358#endif
359 if (link(linktgt, filename) == -1)
360 {
361#ifdef DEBUG
362 perror("link()");
363#endif
Dees_Troyf96fb972013-03-01 22:34:25 +0000364 printf("Failed restore of hardlink '%s' but returning as if nothing bad happened anyway\n", filename);
365 return 0; // Used to be -1
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500366 }
367
368 return 0;
369}
370
371
372/* symlink */
373int
374tar_extract_symlink(TAR *t, char *realname)
375{
376 char *filename;
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500377
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500378 if (!TH_ISSYM(t))
379 {
380 printf("not a sym\n");
381 errno = EINVAL;
382 return -1;
383 }
384
385 filename = (realname ? realname : th_get_pathname(t));
386 printf("file: %s\n", filename);
387 if (mkdirhier(dirname(filename)) == -1) {
388 printf("mkdirhier\n");
389 return -1;
390 }
391
392 if (unlink(filename) == -1 && errno != ENOENT) {
393 printf("unlink\n");
394 return -1;
395 }
396
397#ifdef DEBUG
398 printf(" ==> extracting: %s (symlink to %s)\n",
399 filename, th_get_linkname(t));
400#endif
401 if (symlink(th_get_linkname(t), filename) == -1)
402 {
403#ifdef DEBUG
404 perror("symlink()");
405#endif
406 return -1;
407 }
408
409 return 0;
410}
411
412
413/* character device */
414int
415tar_extract_chardev(TAR *t, char *realname)
416{
417 mode_t mode;
418 unsigned long devmaj, devmin;
419 char *filename;
420
421 if (!TH_ISCHR(t))
422 {
423 errno = EINVAL;
424 return -1;
425 }
426
427 filename = (realname ? realname : th_get_pathname(t));
428 mode = th_get_mode(t);
429 devmaj = th_get_devmajor(t);
430 devmin = th_get_devminor(t);
431
432 if (mkdirhier(dirname(filename)) == -1)
433 return -1;
434
435#ifdef DEBUG
436 printf(" ==> extracting: %s (character device %ld,%ld)\n",
437 filename, devmaj, devmin);
438#endif
439 if (mknod(filename, mode | S_IFCHR,
440 compat_makedev(devmaj, devmin)) == -1)
441 {
442#ifdef DEBUG
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500443 printf("mknod() failed, returning good anyway");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500444#endif
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500445 return 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500446 }
447
448 return 0;
449}
450
451
452/* block device */
453int
454tar_extract_blockdev(TAR *t, char *realname)
455{
456 mode_t mode;
457 unsigned long devmaj, devmin;
458 char *filename;
459
460 if (!TH_ISBLK(t))
461 {
462 errno = EINVAL;
463 return -1;
464 }
465
466 filename = (realname ? realname : th_get_pathname(t));
467 mode = th_get_mode(t);
468 devmaj = th_get_devmajor(t);
469 devmin = th_get_devminor(t);
470
471 if (mkdirhier(dirname(filename)) == -1)
472 return -1;
473
474#ifdef DEBUG
475 printf(" ==> extracting: %s (block device %ld,%ld)\n",
476 filename, devmaj, devmin);
477#endif
478 if (mknod(filename, mode | S_IFBLK,
479 compat_makedev(devmaj, devmin)) == -1)
480 {
481#ifdef DEBUG
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500482 printf("mknod() failed but returning anyway");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500483#endif
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500484 return 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500485 }
486
487 return 0;
488}
489
490
491/* directory */
492int
493tar_extract_dir(TAR *t, char *realname)
494{
495 mode_t mode;
496 char *filename;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500497 if (!TH_ISDIR(t))
498 {
499 errno = EINVAL;
500 return -1;
501 }
502
503 filename = (realname ? realname : th_get_pathname(t));
504 mode = th_get_mode(t);
505
Dees_Troy71796592013-03-14 20:16:29 +0000506 if (mkdirhier(dirname(filename)) == -1) {
507 printf("tar_extract_dir mkdirhier failed\n");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500508 return -1;
Dees_Troy71796592013-03-14 20:16:29 +0000509 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500510
511#ifdef DEBUG
512 printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
513 mode);
514#endif
515 if (mkdir(filename, mode) == -1)
516 {
517 if (errno == EEXIST)
518 {
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500519#ifdef DEBUG
Dees_Troy71796592013-03-14 20:16:29 +0000520 printf(" *** using existing directory");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500521#endif
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500522 }
523 else
524 {
525#ifdef DEBUG
526 perror("mkdir()");
527#endif
528 return -1;
529 }
530 }
531
532 return 0;
533}
534
535
536/* FIFO */
537int
538tar_extract_fifo(TAR *t, char *realname)
539{
540 mode_t mode;
541 char *filename;
542
543 if (!TH_ISFIFO(t))
544 {
545 errno = EINVAL;
546 return -1;
547 }
548
549 filename = (realname ? realname : th_get_pathname(t));
550 mode = th_get_mode(t);
551
552 if (mkdirhier(dirname(filename)) == -1)
553 return -1;
554
555#ifdef DEBUG
556 printf(" ==> extracting: %s (fifo)\n", filename);
557#endif
558 if (mkfifo(filename, mode) == -1)
559 {
560#ifdef DEBUG
561 perror("mkfifo()");
562#endif
563 return -1;
564 }
565
566 return 0;
567}
568
569