2 ** Copyright 1998-2003 University of Illinois Board of Trustees
3 ** Copyright 1998-2003 Mark D. Roth
4 ** All rights reserved.
6 ** extract.c - libtar code to extract a file from a tar archive
8 ** Mark D. Roth <roth@uiuc.edu>
9 ** Campus Information Technologies and Educational Services
10 ** University of Illinois at Urbana-Champaign
13 #include <libtarint/internal.h>
16 #include <libtar/compat.h>
17 #include <sys/types.h>
21 #if defined(_WIN32) && !defined(__CYGWIN__)
23 # include <sys/utime.h>
31 # include <sys/param.h>
43 #ifdef HAVE_SYS_MKDEV_H
44 # include <sys/mkdev.h>
50 char ln_save
[TAR_MAXPATHLEN
];
51 char ln_real
[TAR_MAXPATHLEN
];
53 typedef struct linkname linkname_t
;
57 tar_set_file_perms(TAR
*t
, char *realname
)
72 pathname
= th_get_pathname(t
);
76 mode
= th_get_mode(t
);
79 ut
.modtime
= ut
.actime
= th_get_mtime(t
);
81 /* change owner/group */
85 if (lchown(filename
, uid
, gid
) == -1)
88 fprintf(stderr
, "lchown(\"%s\", %d, %d): %s\n",
89 filename
, uid
, gid
, strerror(errno
));
91 #else /* ! HAVE_LCHOWN */
92 if (!TH_ISSYM(t
) && chown(filename
, uid
, gid
) == -1)
95 fprintf(stderr
, "chown(\"%s\", %d, %d): %s\n",
96 filename
, uid
, gid
, strerror(errno
));
98 #endif /* HAVE_LCHOWN */
106 /* change access/modification time */
107 if (!TH_ISSYM(t
) && utime(filename
, &ut
) == -1)
118 /* change permissions */
119 if (!TH_ISSYM(t
) && chmod(filename
, mode
& 07777) == -1)
148 tar_extract_file(TAR
*t
, char *realname
)
154 if (t
->options
& TAR_NOOVERWRITE
)
159 if (stat(realname
, &s
) == 0 || errno
!= ENOENT
)
161 if (lstat(realname
, &s
) == 0 || errno
!= ENOENT
)
171 i
= tar_extract_dir(t
, realname
);
176 else if (TH_ISLNK(t
))
177 i
= tar_extract_hardlink(t
, realname
);
178 else if (TH_ISSYM(t
))
179 i
= tar_extract_symlink(t
, realname
);
180 else if (TH_ISCHR(t
))
181 i
= tar_extract_chardev(t
, realname
);
182 else if (TH_ISBLK(t
))
183 i
= tar_extract_blockdev(t
, realname
);
184 else if (TH_ISFIFO(t
))
185 i
= tar_extract_fifo(t
, realname
);
187 else /* if (TH_ISREG(t)) */
188 i
= tar_extract_regfile(t
, realname
);
193 i
= tar_set_file_perms(t
, realname
);
197 lnp
= (linkname_t
*)calloc(1, sizeof(linkname_t
));
200 pathname
= th_get_pathname(t
);
201 strlcpy(lnp
->ln_save
, pathname
, sizeof(lnp
->ln_save
));
202 strlcpy(lnp
->ln_real
, realname
, sizeof(lnp
->ln_real
));
204 printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
205 "value=\"%s\"\n", pathname
, realname
);
211 if (libtar_hash_add(t
->h
, lnp
) != 0)
218 /* extract regular file */
220 tar_extract_regfile(TAR
*t
, char *realname
)
228 char buf
[T_BLOCKSIZE
];
233 printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t
,
249 pathname
= th_get_pathname(t
);
252 mode
= th_get_mode(t
);
253 size
= th_get_size(t
);
257 /* Make a copy of the string because dirname and mkdirhier may modify the
259 strncpy(buf
, filename
, sizeof(buf
)-1);
260 buf
[sizeof(buf
)-1] = 0;
262 if (mkdirhier(dirname(buf
)) == -1)
272 printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
273 filename
, mode
, uid
, gid
, size
);
275 fdout
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
293 /* change the owner. (will only work if run as root) */
294 if (fchown(fdout
, uid
, gid
) == -1 && errno
!= EPERM
)
306 /* make sure the mode isn't inheritted from a file we're overwriting */
307 if (fchmod(fdout
, mode
& 07777) == -1)
320 /* extract the file */
321 for (i
= size
; i
> 0; i
-= T_BLOCKSIZE
)
323 k
= tar_block_read(t
, buf
);
324 if (k
!= T_BLOCKSIZE
)
335 /* write block to output file */
336 if (write(fdout
, buf
,
337 ((i
> T_BLOCKSIZE
) ? T_BLOCKSIZE
: (unsigned int)i
)) == -1)
347 /* close output file */
348 if (close(fdout
) == -1)
358 printf("### done extracting %s\n", filename
);
376 tar_skip_regfile(TAR
*t
)
380 char buf
[T_BLOCKSIZE
];
388 size
= th_get_size(t
);
389 for (i
= size
; i
> 0; i
-= T_BLOCKSIZE
)
391 k
= tar_block_read(t
, buf
);
392 if (k
!= T_BLOCKSIZE
)
406 tar_extract_hardlink(TAR
* t
, char *realname
)
412 char buf
[T_BLOCKSIZE
];
427 pathname
= th_get_pathname(t
);
431 /* Make a copy of the string because dirname and mkdirhier may modify the
433 strncpy(buf
, filename
, sizeof(buf
)-1);
434 buf
[sizeof(buf
)-1] = 0;
436 if (mkdirhier(dirname(buf
)) == -1)
444 libtar_hashptr_reset(&hp
);
445 if (libtar_hash_getkey(t
->h
, &hp
, th_get_linkname(t
),
446 (libtar_matchfunc_t
)libtar_str_match
) != 0)
448 lnp
= (linkname_t
*)libtar_hashptr_data(&hp
);
449 linktgt
= lnp
->ln_real
;
452 linktgt
= th_get_linkname(t
);
455 printf(" ==> extracting: %s (link to %s)\n", filename
, linktgt
);
458 if (link(linktgt
, filename
) == -1)
485 tar_extract_symlink(TAR
*t
, char *realname
)
488 char buf
[T_BLOCKSIZE
];
505 pathname
= th_get_pathname(t
);
509 /* Make a copy of the string because dirname and mkdirhier may modify the
511 strncpy(buf
, filename
, sizeof(buf
)-1);
512 buf
[sizeof(buf
)-1] = 0;
514 if (mkdirhier(dirname(buf
)) == -1)
523 if (unlink(filename
) == -1 && errno
!= ENOENT
)
533 printf(" ==> extracting: %s (symlink to %s)\n",
534 filename
, th_get_linkname(t
));
537 if (symlink(th_get_linkname(t
), filename
) == -1)
560 /* character device */
562 tar_extract_chardev(TAR
*t
, char *realname
)
565 unsigned long devmaj
, devmin
;
567 char buf
[T_BLOCKSIZE
];
583 pathname
= th_get_pathname(t
);
586 mode
= th_get_mode(t
);
587 devmaj
= th_get_devmajor(t
);
588 devmin
= th_get_devminor(t
);
590 /* Make a copy of the string because dirname and mkdirhier may modify the
592 strncpy(buf
, filename
, sizeof(buf
)-1);
593 buf
[sizeof(buf
)-1] = 0;
595 if (mkdirhier(dirname(buf
)) == -1)
605 printf(" ==> extracting: %s (character device %ld,%ld)\n",
606 filename
, devmaj
, devmin
);
609 if (mknod(filename
, mode
| S_IFCHR
,
610 compat_makedev(devmaj
, devmin
)) == -1)
639 tar_extract_blockdev(TAR
*t
, char *realname
)
642 unsigned long devmaj
, devmin
;
644 char buf
[T_BLOCKSIZE
];
659 pathname
= th_get_pathname(t
);
662 mode
= th_get_mode(t
);
663 devmaj
= th_get_devmajor(t
);
664 devmin
= th_get_devminor(t
);
666 /* Make a copy of the string because dirname and mkdirhier may modify the
668 strncpy(buf
, filename
, sizeof(buf
)-1);
669 buf
[sizeof(buf
)-1] = 0;
671 if (mkdirhier(dirname(buf
)) == -1)
681 printf(" ==> extracting: %s (block device %ld,%ld)\n",
682 filename
, devmaj
, devmin
);
685 if (mknod(filename
, mode
| S_IFBLK
,
686 compat_makedev(devmaj
, devmin
)) == -1)
715 tar_extract_dir(TAR
*t
, char *realname
)
719 char buf
[T_BLOCKSIZE
];
735 pathname
= th_get_pathname(t
);
738 mode
= th_get_mode(t
);
740 /* Make a copy of the string because dirname and mkdirhier may modify the
742 strncpy(buf
, filename
, sizeof(buf
)-1);
743 buf
[sizeof(buf
)-1] = 0;
745 if (mkdirhier(dirname(buf
)) == -1)
754 /* Strip trailing '/'...it confuses some Unixes (and BeOS)... */
755 strncpy(buf
, filename
, sizeof(buf
)-1);
756 buf
[sizeof(buf
)-1] = 0;
758 if ((len
> 0) && (buf
[len
-1] == '/'))
764 printf(" ==> extracting: %s (mode %04o, directory)\n", filename
,
768 if (mkdir(buf
) == -1)
770 if (mkdir(buf
, mode
& 07777) == -1)
774 /* There is a bug in the Borland Run time library which makes MKDIR
775 return EACCES when it should return EEXIST
776 if it is some other error besides directory exists
778 if ( errno
== EACCES
)
785 if (chmod(filename
, mode
& 07777) == -1)
799 puts(" *** using existing directory");
831 tar_extract_fifo(TAR
*t
, char *realname
)
835 char buf
[T_BLOCKSIZE
];
850 pathname
= th_get_pathname(t
);
853 mode
= th_get_mode(t
);
855 /* Make a copy of the string because dirname and mkdirhier may modify the
857 strncpy(buf
, filename
, sizeof(buf
)-1);
858 buf
[sizeof(buf
)-1] = 0;
860 if (mkdirhier(dirname(buf
)) == -1)
870 printf(" ==> extracting: %s (fifo)\n", filename
);
873 if (mkfifo(filename
, mode
& 07777) == -1)