blob: d19ba859de98299fd88482cf82d9d1af0350c1cc [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
162 printf(" Restoring SELinux context %s to file %s\n", t->th_buf.selinux_context, realname);
163#endif
164 if(setfilecon(realname, t->th_buf.selinux_context) < 0)
165 fprintf(stderr, "Failed to restore SELinux context %s!\n", strerror(errno));
166 }
167#endif
168
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500169/*
170 pathname_len = strlen(th_get_pathname(t)) + 1;
171 realname_len = strlen(realname) + 1;
172 lnp = (char *)calloc(1, pathname_len + realname_len);
173 if (lnp == NULL)
174 return -1;
175 strcpy(&lnp[0], th_get_pathname(t));
176 strcpy(&lnp[pathname_len], realname);
177#ifdef DEBUG
178 printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
179 "value=\"%s\"\n", th_get_pathname(t), realname);
180#endif
181 if (libtar_hash_add(t->h, lnp) != 0)
182 return -1;
183 free(lnp);
184*/
185 return 0;
186}
187
188
189/* extract regular file */
190int
191tar_extract_regfile(TAR *t, char *realname)
192{
Dees_Troy71796592013-03-14 20:16:29 +0000193 //mode_t mode;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500194 size_t size;
Dees_Troy71796592013-03-14 20:16:29 +0000195 //uid_t uid;
196 //gid_t gid;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500197 int fdout;
198 int i, k;
199 char buf[T_BLOCKSIZE];
200 char *filename;
201
202 fflush(NULL);
203#ifdef DEBUG
204 printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
205 realname);
206#endif
207
208 if (!TH_ISREG(t))
209 {
210 errno = EINVAL;
211 return -1;
212 }
213
214 filename = (realname ? realname : th_get_pathname(t));
Dees_Troy71796592013-03-14 20:16:29 +0000215 //mode = th_get_mode(t);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500216 size = th_get_size(t);
Dees_Troy71796592013-03-14 20:16:29 +0000217 //uid = th_get_uid(t);
218 //gid = th_get_gid(t);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500219
220 if (mkdirhier(dirname(filename)) == -1)
221 return -1;
222
223#ifdef DEBUG
Dees_Troy71796592013-03-14 20:16:29 +0000224 //printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
225 // filename, mode, uid, gid, size);
226 printf(" ==> extracting: %s (file size %d bytes)\n",
227 filename, size);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500228#endif
229 fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
230#ifdef O_BINARY
231 | O_BINARY
232#endif
233 , 0666);
234 if (fdout == -1)
235 {
236#ifdef DEBUG
237 perror("open()");
238#endif
239 return -1;
240 }
241
242#if 0
243 /* change the owner. (will only work if run as root) */
244 if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
245 {
246#ifdef DEBUG
247 perror("fchown()");
248#endif
249 return -1;
250 }
251
252 /* make sure the mode isn't inheritted from a file we're overwriting */
253 if (fchmod(fdout, mode & 07777) == -1)
254 {
255#ifdef DEBUG
256 perror("fchmod()");
257#endif
258 return -1;
259 }
260#endif
261
262 /* extract the file */
263 for (i = size; i > 0; i -= T_BLOCKSIZE)
264 {
265 k = tar_block_read(t, buf);
266 if (k != T_BLOCKSIZE)
267 {
268 if (k != -1)
269 errno = EINVAL;
270 return -1;
271 }
272
273 /* write block to output file */
274 if (write(fdout, buf,
275 ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
276 return -1;
277 }
278
279 /* close output file */
280 if (close(fdout) == -1)
281 return -1;
282
283#ifdef DEBUG
284 printf("### done extracting %s\n", filename);
285#endif
286
287 return 0;
288}
289
290
291/* skip regfile */
292int
293tar_skip_regfile(TAR *t)
294{
295 int i, k;
296 size_t size;
297 char buf[T_BLOCKSIZE];
298
299 if (!TH_ISREG(t))
300 {
301 errno = EINVAL;
302 return -1;
303 }
304
305 size = th_get_size(t);
306 for (i = size; i > 0; i -= T_BLOCKSIZE)
307 {
308 k = tar_block_read(t, buf);
309 if (k != T_BLOCKSIZE)
310 {
311 if (k != -1)
312 errno = EINVAL;
313 return -1;
314 }
315 }
316
317 return 0;
318}
319
320
321/* hardlink */
322int
Dees_Troyee6632c2013-02-27 18:07:32 +0000323tar_extract_hardlink(TAR * t, char *realname, char *prefix)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500324{
325 char *filename;
326 char *linktgt = NULL;
327 char *lnp;
328 libtar_hashptr_t hp;
329
330 if (!TH_ISLNK(t))
331 {
332 errno = EINVAL;
333 return -1;
334 }
335
336 filename = (realname ? realname : th_get_pathname(t));
337 if (mkdirhier(dirname(filename)) == -1)
338 return -1;
339 libtar_hashptr_reset(&hp);
340 if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
341 (libtar_matchfunc_t)libtar_str_match) != 0)
342 {
343 lnp = (char *)libtar_hashptr_data(&hp);
344 linktgt = &lnp[strlen(lnp) + 1];
345 }
346 else
347 linktgt = th_get_linkname(t);
Dees_Troyee6632c2013-02-27 18:07:32 +0000348 char *newtgt = strdup(linktgt);
349 sprintf(linktgt, "%s/%s", prefix, newtgt);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500350#ifdef DEBUG
351 printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
352#endif
353 if (link(linktgt, filename) == -1)
354 {
355#ifdef DEBUG
356 perror("link()");
357#endif
Dees_Troyf96fb972013-03-01 22:34:25 +0000358 printf("Failed restore of hardlink '%s' but returning as if nothing bad happened anyway\n", filename);
359 return 0; // Used to be -1
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500360 }
361
362 return 0;
363}
364
365
366/* symlink */
367int
368tar_extract_symlink(TAR *t, char *realname)
369{
370 char *filename;
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500371
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500372 if (!TH_ISSYM(t))
373 {
374 printf("not a sym\n");
375 errno = EINVAL;
376 return -1;
377 }
378
379 filename = (realname ? realname : th_get_pathname(t));
380 printf("file: %s\n", filename);
381 if (mkdirhier(dirname(filename)) == -1) {
382 printf("mkdirhier\n");
383 return -1;
384 }
385
386 if (unlink(filename) == -1 && errno != ENOENT) {
387 printf("unlink\n");
388 return -1;
389 }
390
391#ifdef DEBUG
392 printf(" ==> extracting: %s (symlink to %s)\n",
393 filename, th_get_linkname(t));
394#endif
395 if (symlink(th_get_linkname(t), filename) == -1)
396 {
397#ifdef DEBUG
398 perror("symlink()");
399#endif
400 return -1;
401 }
402
403 return 0;
404}
405
406
407/* character device */
408int
409tar_extract_chardev(TAR *t, char *realname)
410{
411 mode_t mode;
412 unsigned long devmaj, devmin;
413 char *filename;
414
415 if (!TH_ISCHR(t))
416 {
417 errno = EINVAL;
418 return -1;
419 }
420
421 filename = (realname ? realname : th_get_pathname(t));
422 mode = th_get_mode(t);
423 devmaj = th_get_devmajor(t);
424 devmin = th_get_devminor(t);
425
426 if (mkdirhier(dirname(filename)) == -1)
427 return -1;
428
429#ifdef DEBUG
430 printf(" ==> extracting: %s (character device %ld,%ld)\n",
431 filename, devmaj, devmin);
432#endif
433 if (mknod(filename, mode | S_IFCHR,
434 compat_makedev(devmaj, devmin)) == -1)
435 {
436#ifdef DEBUG
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500437 printf("mknod() failed, returning good anyway");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500438#endif
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500439 return 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500440 }
441
442 return 0;
443}
444
445
446/* block device */
447int
448tar_extract_blockdev(TAR *t, char *realname)
449{
450 mode_t mode;
451 unsigned long devmaj, devmin;
452 char *filename;
453
454 if (!TH_ISBLK(t))
455 {
456 errno = EINVAL;
457 return -1;
458 }
459
460 filename = (realname ? realname : th_get_pathname(t));
461 mode = th_get_mode(t);
462 devmaj = th_get_devmajor(t);
463 devmin = th_get_devminor(t);
464
465 if (mkdirhier(dirname(filename)) == -1)
466 return -1;
467
468#ifdef DEBUG
469 printf(" ==> extracting: %s (block device %ld,%ld)\n",
470 filename, devmaj, devmin);
471#endif
472 if (mknod(filename, mode | S_IFBLK,
473 compat_makedev(devmaj, devmin)) == -1)
474 {
475#ifdef DEBUG
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500476 printf("mknod() failed but returning anyway");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500477#endif
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500478 return 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500479 }
480
481 return 0;
482}
483
484
485/* directory */
486int
487tar_extract_dir(TAR *t, char *realname)
488{
489 mode_t mode;
490 char *filename;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500491 if (!TH_ISDIR(t))
492 {
493 errno = EINVAL;
494 return -1;
495 }
496
497 filename = (realname ? realname : th_get_pathname(t));
498 mode = th_get_mode(t);
499
Dees_Troy71796592013-03-14 20:16:29 +0000500 if (mkdirhier(dirname(filename)) == -1) {
501 printf("tar_extract_dir mkdirhier failed\n");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500502 return -1;
Dees_Troy71796592013-03-14 20:16:29 +0000503 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500504
505#ifdef DEBUG
506 printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
507 mode);
508#endif
509 if (mkdir(filename, mode) == -1)
510 {
511 if (errno == EEXIST)
512 {
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500513#ifdef DEBUG
Dees_Troy71796592013-03-14 20:16:29 +0000514 printf(" *** using existing directory");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500515#endif
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500516 }
517 else
518 {
519#ifdef DEBUG
520 perror("mkdir()");
521#endif
522 return -1;
523 }
524 }
525
526 return 0;
527}
528
529
530/* FIFO */
531int
532tar_extract_fifo(TAR *t, char *realname)
533{
534 mode_t mode;
535 char *filename;
536
537 if (!TH_ISFIFO(t))
538 {
539 errno = EINVAL;
540 return -1;
541 }
542
543 filename = (realname ? realname : th_get_pathname(t));
544 mode = th_get_mode(t);
545
546 if (mkdirhier(dirname(filename)) == -1)
547 return -1;
548
549#ifdef DEBUG
550 printf(" ==> extracting: %s (fifo)\n", filename);
551#endif
552 if (mkfifo(filename, mode) == -1)
553 {
554#ifdef DEBUG
555 perror("mkfifo()");
556#endif
557 return -1;
558 }
559
560 return 0;
561}
562
563