blob: 55442871522bebde73e40632e1a8e07f43085f7e [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
92tar_extract_file(TAR *t, char *realname)
93{
94 int i;
95 char *lnp;
96 int pathname_len;
97 int realname_len;
98
99 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");
118 i = tar_extract_hardlink(t, realname);
119 }
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) {
142 printf("here i: %d\n", i);
143 return i;
144 }
145
146 i = tar_set_file_perms(t, realname);
147 if (i != 0) {
148 printf("i: %d\n", i);
149 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
303tar_extract_hardlink(TAR * t, char *realname)
304{
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);
328
329#ifdef DEBUG
330 printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
331#endif
332 if (link(linktgt, filename) == -1)
333 {
334#ifdef DEBUG
335 perror("link()");
336#endif
337 return -1;
338 }
339
340 return 0;
341}
342
343
344/* symlink */
345int
346tar_extract_symlink(TAR *t, char *realname)
347{
348 char *filename;
349
350 if (!TH_ISSYM(t))
351 {
352 printf("not a sym\n");
353 errno = EINVAL;
354 return -1;
355 }
356
357 filename = (realname ? realname : th_get_pathname(t));
358 printf("file: %s\n", filename);
359 if (mkdirhier(dirname(filename)) == -1) {
360 printf("mkdirhier\n");
361 return -1;
362 }
363
364 if (unlink(filename) == -1 && errno != ENOENT) {
365 printf("unlink\n");
366 return -1;
367 }
368
369#ifdef DEBUG
370 printf(" ==> extracting: %s (symlink to %s)\n",
371 filename, th_get_linkname(t));
372#endif
373 if (symlink(th_get_linkname(t), filename) == -1)
374 {
375#ifdef DEBUG
376 perror("symlink()");
377#endif
378 return -1;
379 }
380
381 return 0;
382}
383
384
385/* character device */
386int
387tar_extract_chardev(TAR *t, char *realname)
388{
389 mode_t mode;
390 unsigned long devmaj, devmin;
391 char *filename;
392
393 if (!TH_ISCHR(t))
394 {
395 errno = EINVAL;
396 return -1;
397 }
398
399 filename = (realname ? realname : th_get_pathname(t));
400 mode = th_get_mode(t);
401 devmaj = th_get_devmajor(t);
402 devmin = th_get_devminor(t);
403
404 if (mkdirhier(dirname(filename)) == -1)
405 return -1;
406
407#ifdef DEBUG
408 printf(" ==> extracting: %s (character device %ld,%ld)\n",
409 filename, devmaj, devmin);
410#endif
411 if (mknod(filename, mode | S_IFCHR,
412 compat_makedev(devmaj, devmin)) == -1)
413 {
414#ifdef DEBUG
415 perror("mknod()");
416#endif
417 return -1;
418 }
419
420 return 0;
421}
422
423
424/* block device */
425int
426tar_extract_blockdev(TAR *t, char *realname)
427{
428 mode_t mode;
429 unsigned long devmaj, devmin;
430 char *filename;
431
432 if (!TH_ISBLK(t))
433 {
434 errno = EINVAL;
435 return -1;
436 }
437
438 filename = (realname ? realname : th_get_pathname(t));
439 mode = th_get_mode(t);
440 devmaj = th_get_devmajor(t);
441 devmin = th_get_devminor(t);
442
443 if (mkdirhier(dirname(filename)) == -1)
444 return -1;
445
446#ifdef DEBUG
447 printf(" ==> extracting: %s (block device %ld,%ld)\n",
448 filename, devmaj, devmin);
449#endif
450 if (mknod(filename, mode | S_IFBLK,
451 compat_makedev(devmaj, devmin)) == -1)
452 {
453#ifdef DEBUG
454 perror("mknod()");
455#endif
456 return -1;
457 }
458
459 return 0;
460}
461
462
463/* directory */
464int
465tar_extract_dir(TAR *t, char *realname)
466{
467 mode_t mode;
468 char *filename;
469 printf("filename: %s\n", filename);
470 if (!TH_ISDIR(t))
471 {
472 errno = EINVAL;
473 return -1;
474 }
475
476 filename = (realname ? realname : th_get_pathname(t));
477 mode = th_get_mode(t);
478
479 if (mkdirhier(dirname(filename)) == -1)
480 return -1;
481
482#ifdef DEBUG
483 printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
484 mode);
485#endif
486 if (mkdir(filename, mode) == -1)
487 {
488 if (errno == EEXIST)
489 {
490 if (chmod(filename, mode) == -1)
491 {
492#ifdef DEBUG
493 perror("chmod()");
494#endif
495 return -1;
496 }
497 else
498 {
499#ifdef DEBUG
500 puts(" *** using existing directory");
501#endif
502 return 1;
503 }
504 }
505 else
506 {
507#ifdef DEBUG
508 perror("mkdir()");
509#endif
510 return -1;
511 }
512 }
513
514 return 0;
515}
516
517
518/* FIFO */
519int
520tar_extract_fifo(TAR *t, char *realname)
521{
522 mode_t mode;
523 char *filename;
524
525 if (!TH_ISFIFO(t))
526 {
527 errno = EINVAL;
528 return -1;
529 }
530
531 filename = (realname ? realname : th_get_pathname(t));
532 mode = th_get_mode(t);
533
534 if (mkdirhier(dirname(filename)) == -1)
535 return -1;
536
537#ifdef DEBUG
538 printf(" ==> extracting: %s (fifo)\n", filename);
539#endif
540 if (mkfifo(filename, mode) == -1)
541 {
542#ifdef DEBUG
543 perror("mkfifo()");
544#endif
545 return -1;
546 }
547
548 return 0;
549}
550
551