blob: d9ab7882f0cb98f3d422db9627b60b6793887950 [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
Dees_Troyee6632c2013-02-27 18:07:32 +000097tar_extract_file(TAR *t, char *realname, char *prefix)
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");
144 i = tar_extract_regfile(t, realname);
145 }
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 }
bigbiff bigbiff6b600f92014-01-05 18:13:43 -0500167 if (lsetfilecon(dirname(realname), t->th_buf.selinux_context) < 0) {
168 fprintf(stderr, "Failed to restore SELinux context %s!\n", strerror(errno));
169 }
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200170 }
171#endif
172
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500173/*
174 pathname_len = strlen(th_get_pathname(t)) + 1;
175 realname_len = strlen(realname) + 1;
176 lnp = (char *)calloc(1, pathname_len + realname_len);
177 if (lnp == NULL)
178 return -1;
179 strcpy(&lnp[0], th_get_pathname(t));
180 strcpy(&lnp[pathname_len], realname);
181#ifdef DEBUG
182 printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
183 "value=\"%s\"\n", th_get_pathname(t), realname);
184#endif
185 if (libtar_hash_add(t->h, lnp) != 0)
186 return -1;
187 free(lnp);
188*/
189 return 0;
190}
191
192
193/* extract regular file */
194int
195tar_extract_regfile(TAR *t, char *realname)
196{
Dees_Troy71796592013-03-14 20:16:29 +0000197 //mode_t mode;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500198 size_t size;
Dees_Troy71796592013-03-14 20:16:29 +0000199 //uid_t uid;
200 //gid_t gid;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500201 int fdout;
202 int i, k;
203 char buf[T_BLOCKSIZE];
204 char *filename;
205
206 fflush(NULL);
207#ifdef DEBUG
208 printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
209 realname);
210#endif
211
212 if (!TH_ISREG(t))
213 {
214 errno = EINVAL;
215 return -1;
216 }
217
218 filename = (realname ? realname : th_get_pathname(t));
Dees_Troy71796592013-03-14 20:16:29 +0000219 //mode = th_get_mode(t);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500220 size = th_get_size(t);
Dees_Troy71796592013-03-14 20:16:29 +0000221 //uid = th_get_uid(t);
222 //gid = th_get_gid(t);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500223
224 if (mkdirhier(dirname(filename)) == -1)
225 return -1;
226
227#ifdef DEBUG
Dees_Troy71796592013-03-14 20:16:29 +0000228 //printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
229 // filename, mode, uid, gid, size);
230 printf(" ==> extracting: %s (file size %d bytes)\n",
231 filename, size);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500232#endif
233 fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
234#ifdef O_BINARY
235 | O_BINARY
236#endif
237 , 0666);
238 if (fdout == -1)
239 {
240#ifdef DEBUG
241 perror("open()");
242#endif
243 return -1;
244 }
245
246#if 0
247 /* change the owner. (will only work if run as root) */
248 if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
249 {
250#ifdef DEBUG
251 perror("fchown()");
252#endif
253 return -1;
254 }
255
256 /* make sure the mode isn't inheritted from a file we're overwriting */
257 if (fchmod(fdout, mode & 07777) == -1)
258 {
259#ifdef DEBUG
260 perror("fchmod()");
261#endif
262 return -1;
263 }
264#endif
265
266 /* extract the file */
267 for (i = size; i > 0; i -= T_BLOCKSIZE)
268 {
269 k = tar_block_read(t, buf);
270 if (k != T_BLOCKSIZE)
271 {
272 if (k != -1)
273 errno = EINVAL;
274 return -1;
275 }
276
277 /* write block to output file */
278 if (write(fdout, buf,
279 ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
280 return -1;
281 }
282
283 /* close output file */
284 if (close(fdout) == -1)
285 return -1;
286
287#ifdef DEBUG
288 printf("### done extracting %s\n", filename);
289#endif
290
291 return 0;
292}
293
294
295/* skip regfile */
296int
297tar_skip_regfile(TAR *t)
298{
299 int i, k;
300 size_t size;
301 char buf[T_BLOCKSIZE];
302
303 if (!TH_ISREG(t))
304 {
305 errno = EINVAL;
306 return -1;
307 }
308
309 size = th_get_size(t);
310 for (i = size; i > 0; i -= T_BLOCKSIZE)
311 {
312 k = tar_block_read(t, buf);
313 if (k != T_BLOCKSIZE)
314 {
315 if (k != -1)
316 errno = EINVAL;
317 return -1;
318 }
319 }
320
321 return 0;
322}
323
324
325/* hardlink */
326int
Dees_Troyee6632c2013-02-27 18:07:32 +0000327tar_extract_hardlink(TAR * t, char *realname, char *prefix)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500328{
329 char *filename;
330 char *linktgt = NULL;
331 char *lnp;
332 libtar_hashptr_t hp;
333
334 if (!TH_ISLNK(t))
335 {
336 errno = EINVAL;
337 return -1;
338 }
339
340 filename = (realname ? realname : th_get_pathname(t));
341 if (mkdirhier(dirname(filename)) == -1)
342 return -1;
343 libtar_hashptr_reset(&hp);
344 if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
345 (libtar_matchfunc_t)libtar_str_match) != 0)
346 {
347 lnp = (char *)libtar_hashptr_data(&hp);
348 linktgt = &lnp[strlen(lnp) + 1];
349 }
350 else
351 linktgt = th_get_linkname(t);
Dees_Troyee6632c2013-02-27 18:07:32 +0000352 char *newtgt = strdup(linktgt);
353 sprintf(linktgt, "%s/%s", prefix, newtgt);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500354#ifdef DEBUG
355 printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
356#endif
357 if (link(linktgt, filename) == -1)
358 {
359#ifdef DEBUG
360 perror("link()");
361#endif
Dees_Troyf96fb972013-03-01 22:34:25 +0000362 printf("Failed restore of hardlink '%s' but returning as if nothing bad happened anyway\n", filename);
363 return 0; // Used to be -1
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500364 }
365
366 return 0;
367}
368
369
370/* symlink */
371int
372tar_extract_symlink(TAR *t, char *realname)
373{
374 char *filename;
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500375
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500376 if (!TH_ISSYM(t))
377 {
378 printf("not a sym\n");
379 errno = EINVAL;
380 return -1;
381 }
382
383 filename = (realname ? realname : th_get_pathname(t));
384 printf("file: %s\n", filename);
385 if (mkdirhier(dirname(filename)) == -1) {
386 printf("mkdirhier\n");
387 return -1;
388 }
389
390 if (unlink(filename) == -1 && errno != ENOENT) {
391 printf("unlink\n");
392 return -1;
393 }
394
395#ifdef DEBUG
396 printf(" ==> extracting: %s (symlink to %s)\n",
397 filename, th_get_linkname(t));
398#endif
399 if (symlink(th_get_linkname(t), filename) == -1)
400 {
401#ifdef DEBUG
402 perror("symlink()");
403#endif
404 return -1;
405 }
406
407 return 0;
408}
409
410
411/* character device */
412int
413tar_extract_chardev(TAR *t, char *realname)
414{
415 mode_t mode;
416 unsigned long devmaj, devmin;
417 char *filename;
418
419 if (!TH_ISCHR(t))
420 {
421 errno = EINVAL;
422 return -1;
423 }
424
425 filename = (realname ? realname : th_get_pathname(t));
426 mode = th_get_mode(t);
427 devmaj = th_get_devmajor(t);
428 devmin = th_get_devminor(t);
429
430 if (mkdirhier(dirname(filename)) == -1)
431 return -1;
432
433#ifdef DEBUG
434 printf(" ==> extracting: %s (character device %ld,%ld)\n",
435 filename, devmaj, devmin);
436#endif
437 if (mknod(filename, mode | S_IFCHR,
438 compat_makedev(devmaj, devmin)) == -1)
439 {
440#ifdef DEBUG
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500441 printf("mknod() failed, returning good anyway");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500442#endif
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500443 return 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500444 }
445
446 return 0;
447}
448
449
450/* block device */
451int
452tar_extract_blockdev(TAR *t, char *realname)
453{
454 mode_t mode;
455 unsigned long devmaj, devmin;
456 char *filename;
457
458 if (!TH_ISBLK(t))
459 {
460 errno = EINVAL;
461 return -1;
462 }
463
464 filename = (realname ? realname : th_get_pathname(t));
465 mode = th_get_mode(t);
466 devmaj = th_get_devmajor(t);
467 devmin = th_get_devminor(t);
468
469 if (mkdirhier(dirname(filename)) == -1)
470 return -1;
471
472#ifdef DEBUG
473 printf(" ==> extracting: %s (block device %ld,%ld)\n",
474 filename, devmaj, devmin);
475#endif
476 if (mknod(filename, mode | S_IFBLK,
477 compat_makedev(devmaj, devmin)) == -1)
478 {
479#ifdef DEBUG
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500480 printf("mknod() failed but returning anyway");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500481#endif
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500482 return 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500483 }
484
485 return 0;
486}
487
488
489/* directory */
490int
491tar_extract_dir(TAR *t, char *realname)
492{
493 mode_t mode;
494 char *filename;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500495 if (!TH_ISDIR(t))
496 {
497 errno = EINVAL;
498 return -1;
499 }
500
501 filename = (realname ? realname : th_get_pathname(t));
502 mode = th_get_mode(t);
503
Dees_Troy71796592013-03-14 20:16:29 +0000504 if (mkdirhier(dirname(filename)) == -1) {
505 printf("tar_extract_dir mkdirhier failed\n");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500506 return -1;
Dees_Troy71796592013-03-14 20:16:29 +0000507 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500508
509#ifdef DEBUG
510 printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
511 mode);
512#endif
513 if (mkdir(filename, mode) == -1)
514 {
515 if (errno == EEXIST)
516 {
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500517#ifdef DEBUG
Dees_Troy71796592013-03-14 20:16:29 +0000518 printf(" *** using existing directory");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500519#endif
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500520 }
521 else
522 {
523#ifdef DEBUG
524 perror("mkdir()");
525#endif
526 return -1;
527 }
528 }
529
530 return 0;
531}
532
533
534/* FIFO */
535int
536tar_extract_fifo(TAR *t, char *realname)
537{
538 mode_t mode;
539 char *filename;
540
541 if (!TH_ISFIFO(t))
542 {
543 errno = EINVAL;
544 return -1;
545 }
546
547 filename = (realname ? realname : th_get_pathname(t));
548 mode = th_get_mode(t);
549
550 if (mkdirhier(dirname(filename)) == -1)
551 return -1;
552
553#ifdef DEBUG
554 printf(" ==> extracting: %s (fifo)\n", filename);
555#endif
556 if (mkfifo(filename, mode) == -1)
557 {
558#ifdef DEBUG
559 perror("mkfifo()");
560#endif
561 return -1;
562 }
563
564 return 0;
565}
566
567