blob: 49e7599e814750e8fdae52c29b00c2cc2d353769 [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
48 /* change owner/group */
49 if (geteuid() == 0)
50#ifdef HAVE_LCHOWN
51 if (lchown(filename, uid, gid) == -1)
52 {
53# ifdef DEBUG
54 fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
55 filename, uid, gid, strerror(errno));
56# endif
57#else /* ! HAVE_LCHOWN */
58 if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
59 {
60# ifdef DEBUG
61 fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
62 filename, uid, gid, strerror(errno));
63# endif
64#endif /* HAVE_LCHOWN */
65 return -1;
66 }
67
68 /* change access/modification time */
69 if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
70 {
71#ifdef DEBUG
72 perror("utime()");
73#endif
74 return -1;
75 }
76
77 /* change permissions */
78 if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
79 {
80#ifdef DEBUG
81 perror("chmod()");
82#endif
83 return -1;
84 }
85
86 return 0;
87}
88
89
90/* switchboard */
91int
Dees_Troyee6632c2013-02-27 18:07:32 +000092tar_extract_file(TAR *t, char *realname, char *prefix)
bigbiff bigbiff9c754052013-01-09 09:09:08 -050093{
94 int i;
95 char *lnp;
96 int pathname_len;
97 int realname_len;
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -050098
bigbiff bigbiff9c754052013-01-09 09:09:08 -050099 if (t->options & TAR_NOOVERWRITE)
100 {
101 struct stat s;
102
103 if (lstat(realname, &s) == 0 || errno != ENOENT)
104 {
105 errno = EEXIST;
106 return -1;
107 }
108 }
109
110 if (TH_ISDIR(t))
111 {
112 i = tar_extract_dir(t, realname);
113 if (i == 1)
114 i = 0;
115 }
116 else if (TH_ISLNK(t)) {
117 printf("link\n");
Dees_Troyee6632c2013-02-27 18:07:32 +0000118 i = tar_extract_hardlink(t, realname, prefix);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500119 }
120 else if (TH_ISSYM(t)) {
121 printf("sym\n");
122 i = tar_extract_symlink(t, realname);
123 }
124 else if (TH_ISCHR(t)) {
125 printf("chr\n");
126 i = tar_extract_chardev(t, realname);
127 }
128 else if (TH_ISBLK(t)) {
129 printf("blk\n");
130 i = tar_extract_blockdev(t, realname);
131 }
132 else if (TH_ISFIFO(t)) {
133 printf("fifo\n");
134 i = tar_extract_fifo(t, realname);
135 }
136 else /* if (TH_ISREG(t)) */ {
137 printf("reg\n");
138 i = tar_extract_regfile(t, realname);
139 }
140
141 if (i != 0) {
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500142 printf("FAILED RESTORE OF FILE i: %s\n", realname);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500143 return i;
144 }
145
146 i = tar_set_file_perms(t, realname);
147 if (i != 0) {
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500148 printf("FAILED SETTING PERMS: %d\n", i);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500149 return i;
150 }
151/*
152 pathname_len = strlen(th_get_pathname(t)) + 1;
153 realname_len = strlen(realname) + 1;
154 lnp = (char *)calloc(1, pathname_len + realname_len);
155 if (lnp == NULL)
156 return -1;
157 strcpy(&lnp[0], th_get_pathname(t));
158 strcpy(&lnp[pathname_len], realname);
159#ifdef DEBUG
160 printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
161 "value=\"%s\"\n", th_get_pathname(t), realname);
162#endif
163 if (libtar_hash_add(t->h, lnp) != 0)
164 return -1;
165 free(lnp);
166*/
167 return 0;
168}
169
170
171/* extract regular file */
172int
173tar_extract_regfile(TAR *t, char *realname)
174{
175 mode_t mode;
176 size_t size;
177 uid_t uid;
178 gid_t gid;
179 int fdout;
180 int i, k;
181 char buf[T_BLOCKSIZE];
182 char *filename;
183
184 fflush(NULL);
185#ifdef DEBUG
186 printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
187 realname);
188#endif
189
190 if (!TH_ISREG(t))
191 {
192 errno = EINVAL;
193 return -1;
194 }
195
196 filename = (realname ? realname : th_get_pathname(t));
197 mode = th_get_mode(t);
198 size = th_get_size(t);
199 uid = th_get_uid(t);
200 gid = th_get_gid(t);
201
202 if (mkdirhier(dirname(filename)) == -1)
203 return -1;
204
205#ifdef DEBUG
206 printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
207 filename, mode, uid, gid, size);
208#endif
209 fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
210#ifdef O_BINARY
211 | O_BINARY
212#endif
213 , 0666);
214 if (fdout == -1)
215 {
216#ifdef DEBUG
217 perror("open()");
218#endif
219 return -1;
220 }
221
222#if 0
223 /* change the owner. (will only work if run as root) */
224 if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
225 {
226#ifdef DEBUG
227 perror("fchown()");
228#endif
229 return -1;
230 }
231
232 /* make sure the mode isn't inheritted from a file we're overwriting */
233 if (fchmod(fdout, mode & 07777) == -1)
234 {
235#ifdef DEBUG
236 perror("fchmod()");
237#endif
238 return -1;
239 }
240#endif
241
242 /* extract the file */
243 for (i = size; i > 0; i -= T_BLOCKSIZE)
244 {
245 k = tar_block_read(t, buf);
246 if (k != T_BLOCKSIZE)
247 {
248 if (k != -1)
249 errno = EINVAL;
250 return -1;
251 }
252
253 /* write block to output file */
254 if (write(fdout, buf,
255 ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
256 return -1;
257 }
258
259 /* close output file */
260 if (close(fdout) == -1)
261 return -1;
262
263#ifdef DEBUG
264 printf("### done extracting %s\n", filename);
265#endif
266
267 return 0;
268}
269
270
271/* skip regfile */
272int
273tar_skip_regfile(TAR *t)
274{
275 int i, k;
276 size_t size;
277 char buf[T_BLOCKSIZE];
278
279 if (!TH_ISREG(t))
280 {
281 errno = EINVAL;
282 return -1;
283 }
284
285 size = th_get_size(t);
286 for (i = size; i > 0; i -= T_BLOCKSIZE)
287 {
288 k = tar_block_read(t, buf);
289 if (k != T_BLOCKSIZE)
290 {
291 if (k != -1)
292 errno = EINVAL;
293 return -1;
294 }
295 }
296
297 return 0;
298}
299
300
301/* hardlink */
302int
Dees_Troyee6632c2013-02-27 18:07:32 +0000303tar_extract_hardlink(TAR * t, char *realname, char *prefix)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500304{
305 char *filename;
306 char *linktgt = NULL;
307 char *lnp;
308 libtar_hashptr_t hp;
309
310 if (!TH_ISLNK(t))
311 {
312 errno = EINVAL;
313 return -1;
314 }
315
316 filename = (realname ? realname : th_get_pathname(t));
317 if (mkdirhier(dirname(filename)) == -1)
318 return -1;
319 libtar_hashptr_reset(&hp);
320 if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
321 (libtar_matchfunc_t)libtar_str_match) != 0)
322 {
323 lnp = (char *)libtar_hashptr_data(&hp);
324 linktgt = &lnp[strlen(lnp) + 1];
325 }
326 else
327 linktgt = th_get_linkname(t);
Dees_Troyee6632c2013-02-27 18:07:32 +0000328 char *newtgt = strdup(linktgt);
329 sprintf(linktgt, "%s/%s", prefix, newtgt);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500330#ifdef DEBUG
331 printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
332#endif
333 if (link(linktgt, filename) == -1)
334 {
335#ifdef DEBUG
336 perror("link()");
337#endif
Dees_Troyf96fb972013-03-01 22:34:25 +0000338 printf("Failed restore of hardlink '%s' but returning as if nothing bad happened anyway\n", filename);
339 return 0; // Used to be -1
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500340 }
341
342 return 0;
343}
344
345
346/* symlink */
347int
348tar_extract_symlink(TAR *t, char *realname)
349{
350 char *filename;
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500351
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500352 if (!TH_ISSYM(t))
353 {
354 printf("not a sym\n");
355 errno = EINVAL;
356 return -1;
357 }
358
359 filename = (realname ? realname : th_get_pathname(t));
360 printf("file: %s\n", filename);
361 if (mkdirhier(dirname(filename)) == -1) {
362 printf("mkdirhier\n");
363 return -1;
364 }
365
366 if (unlink(filename) == -1 && errno != ENOENT) {
367 printf("unlink\n");
368 return -1;
369 }
370
371#ifdef DEBUG
372 printf(" ==> extracting: %s (symlink to %s)\n",
373 filename, th_get_linkname(t));
374#endif
375 if (symlink(th_get_linkname(t), filename) == -1)
376 {
377#ifdef DEBUG
378 perror("symlink()");
379#endif
380 return -1;
381 }
382
383 return 0;
384}
385
386
387/* character device */
388int
389tar_extract_chardev(TAR *t, char *realname)
390{
391 mode_t mode;
392 unsigned long devmaj, devmin;
393 char *filename;
394
395 if (!TH_ISCHR(t))
396 {
397 errno = EINVAL;
398 return -1;
399 }
400
401 filename = (realname ? realname : th_get_pathname(t));
402 mode = th_get_mode(t);
403 devmaj = th_get_devmajor(t);
404 devmin = th_get_devminor(t);
405
406 if (mkdirhier(dirname(filename)) == -1)
407 return -1;
408
409#ifdef DEBUG
410 printf(" ==> extracting: %s (character device %ld,%ld)\n",
411 filename, devmaj, devmin);
412#endif
413 if (mknod(filename, mode | S_IFCHR,
414 compat_makedev(devmaj, devmin)) == -1)
415 {
416#ifdef DEBUG
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500417 printf("mknod() failed, returning good anyway");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500418#endif
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500419 return 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500420 }
421
422 return 0;
423}
424
425
426/* block device */
427int
428tar_extract_blockdev(TAR *t, char *realname)
429{
430 mode_t mode;
431 unsigned long devmaj, devmin;
432 char *filename;
433
434 if (!TH_ISBLK(t))
435 {
436 errno = EINVAL;
437 return -1;
438 }
439
440 filename = (realname ? realname : th_get_pathname(t));
441 mode = th_get_mode(t);
442 devmaj = th_get_devmajor(t);
443 devmin = th_get_devminor(t);
444
445 if (mkdirhier(dirname(filename)) == -1)
446 return -1;
447
448#ifdef DEBUG
449 printf(" ==> extracting: %s (block device %ld,%ld)\n",
450 filename, devmaj, devmin);
451#endif
452 if (mknod(filename, mode | S_IFBLK,
453 compat_makedev(devmaj, devmin)) == -1)
454 {
455#ifdef DEBUG
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500456 printf("mknod() failed but returning anyway");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500457#endif
bigbiff bigbiff71e5aa42013-02-26 20:10:16 -0500458 return 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500459 }
460
461 return 0;
462}
463
464
465/* directory */
466int
467tar_extract_dir(TAR *t, char *realname)
468{
469 mode_t mode;
470 char *filename;
471 printf("filename: %s\n", filename);
472 if (!TH_ISDIR(t))
473 {
474 errno = EINVAL;
475 return -1;
476 }
477
478 filename = (realname ? realname : th_get_pathname(t));
479 mode = th_get_mode(t);
480
481 if (mkdirhier(dirname(filename)) == -1)
482 return -1;
483
484#ifdef DEBUG
485 printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
486 mode);
487#endif
488 if (mkdir(filename, mode) == -1)
489 {
490 if (errno == EEXIST)
491 {
492 if (chmod(filename, mode) == -1)
493 {
494#ifdef DEBUG
495 perror("chmod()");
496#endif
497 return -1;
498 }
499 else
500 {
501#ifdef DEBUG
502 puts(" *** using existing directory");
503#endif
504 return 1;
505 }
506 }
507 else
508 {
509#ifdef DEBUG
510 perror("mkdir()");
511#endif
512 return -1;
513 }
514 }
515
516 return 0;
517}
518
519
520/* FIFO */
521int
522tar_extract_fifo(TAR *t, char *realname)
523{
524 mode_t mode;
525 char *filename;
526
527 if (!TH_ISFIFO(t))
528 {
529 errno = EINVAL;
530 return -1;
531 }
532
533 filename = (realname ? realname : th_get_pathname(t));
534 mode = th_get_mode(t);
535
536 if (mkdirhier(dirname(filename)) == -1)
537 return -1;
538
539#ifdef DEBUG
540 printf(" ==> extracting: %s (fifo)\n", filename);
541#endif
542 if (mkfifo(filename, mode) == -1)
543 {
544#ifdef DEBUG
545 perror("mkfifo()");
546#endif
547 return -1;
548 }
549
550 return 0;
551}
552
553