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 # ifdef HAVE_SYS_PARAM_H
32 # include <sys/param.h>
45 #ifdef HAVE_SYS_MKDEV_H
46 # include <sys/mkdev.h>
52 char ln_save
[TAR_MAXPATHLEN
];
53 char ln_real
[TAR_MAXPATHLEN
];
55 typedef struct linkname linkname_t
;
59 tar_set_file_perms(TAR
*t
, char *realname
)
74 pathname
= th_get_pathname(t
);
78 mode
= th_get_mode(t
);
81 ut
.modtime
= ut
.actime
= th_get_mtime(t
);
83 /* change owner/group */
87 if (lchown(filename
, uid
, gid
) == -1)
90 fprintf(stderr
, "lchown(\"%s\", %d, %d): %s\n",
91 filename
, uid
, gid
, strerror(errno
));
93 #else /* ! HAVE_LCHOWN */
94 if (!TH_ISSYM(t
) && chown(filename
, uid
, gid
) == -1)
97 fprintf(stderr
, "chown(\"%s\", %d, %d): %s\n",
98 filename
, uid
, gid
, strerror(errno
));
100 #endif /* HAVE_LCHOWN */
108 /* change access/modification time */
109 if (!TH_ISSYM(t
) && utime(filename
, &ut
) == -1)
120 /* change permissions */
121 if (!TH_ISSYM(t
) && chmod(filename
, mode
& 07777) == -1)
150 tar_extract_file(TAR
*t
, char *realname
)
156 if (t
->options
& TAR_NOOVERWRITE
)
161 if (stat(realname
, &s
) == 0 || errno
!= ENOENT
)
163 if (lstat(realname
, &s
) == 0 || errno
!= ENOENT
)
173 i
= tar_extract_dir(t
, realname
);
178 else if (TH_ISLNK(t
))
179 i
= tar_extract_hardlink(t
, realname
);
180 else if (TH_ISSYM(t
))
181 i
= tar_extract_symlink(t
, realname
);
182 else if (TH_ISCHR(t
))
183 i
= tar_extract_chardev(t
, realname
);
184 else if (TH_ISBLK(t
))
185 i
= tar_extract_blockdev(t
, realname
);
186 else if (TH_ISFIFO(t
))
187 i
= tar_extract_fifo(t
, realname
);
189 else /* if (TH_ISREG(t)) */
190 i
= tar_extract_regfile(t
, realname
);
195 i
= tar_set_file_perms(t
, realname
);
199 lnp
= (linkname_t
*)calloc(1, sizeof(linkname_t
));
202 pathname
= th_get_pathname(t
);
203 strlcpy(lnp
->ln_save
, pathname
, sizeof(lnp
->ln_save
));
204 strlcpy(lnp
->ln_real
, realname
, sizeof(lnp
->ln_real
));
206 printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
207 "value=\"%s\"\n", pathname
, realname
);
213 if (libtar_hash_add(t
->h
, lnp
) != 0)
220 /* extract regular file */
222 tar_extract_regfile(TAR
*t
, char *realname
)
230 char buf
[T_BLOCKSIZE
];
235 printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t
,
251 pathname
= th_get_pathname(t
);
254 mode
= th_get_mode(t
);
255 size
= th_get_size(t
);
259 /* Make a copy of the string because dirname and mkdirhier may modify the
261 strncpy(buf
, filename
, sizeof(buf
)-1);
262 buf
[sizeof(buf
)-1] = 0;
264 if (mkdirhier(dirname(buf
)) == -1)
274 printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
275 filename
, mode
, uid
, gid
, size
);
277 fdout
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
295 /* change the owner. (will only work if run as root) */
296 if (fchown(fdout
, uid
, gid
) == -1 && errno
!= EPERM
)
308 /* make sure the mode isn't inheritted from a file we're overwriting */
309 if (fchmod(fdout
, mode
& 07777) == -1)
322 /* extract the file */
323 for (i
= size
; i
> 0; i
-= T_BLOCKSIZE
)
325 k
= tar_block_read(t
, buf
);
326 if (k
!= T_BLOCKSIZE
)
337 /* write block to output file */
338 if (write(fdout
, buf
,
339 ((i
> T_BLOCKSIZE
) ? T_BLOCKSIZE
: (unsigned int)i
)) == -1)
349 /* close output file */
350 if (close(fdout
) == -1)
360 printf("### done extracting %s\n", filename
);
378 tar_skip_regfile(TAR
*t
)
382 char buf
[T_BLOCKSIZE
];
390 size
= th_get_size(t
);
391 for (i
= size
; i
> 0; i
-= T_BLOCKSIZE
)
393 k
= tar_block_read(t
, buf
);
394 if (k
!= T_BLOCKSIZE
)
408 tar_extract_hardlink(TAR
* t
, char *realname
)
414 char buf
[T_BLOCKSIZE
];
429 pathname
= th_get_pathname(t
);
433 /* Make a copy of the string because dirname and mkdirhier may modify the
435 strncpy(buf
, filename
, sizeof(buf
)-1);
436 buf
[sizeof(buf
)-1] = 0;
438 if (mkdirhier(dirname(buf
)) == -1)
446 libtar_hashptr_reset(&hp
);
447 if (libtar_hash_getkey(t
->h
, &hp
, th_get_linkname(t
),
448 (libtar_matchfunc_t
)libtar_str_match
) != 0)
450 lnp
= (linkname_t
*)libtar_hashptr_data(&hp
);
451 linktgt
= lnp
->ln_real
;
454 linktgt
= th_get_linkname(t
);
457 printf(" ==> extracting: %s (link to %s)\n", filename
, linktgt
);
460 if (link(linktgt
, filename
) == -1)
487 tar_extract_symlink(TAR
*t
, char *realname
)
490 char buf
[T_BLOCKSIZE
];
507 pathname
= th_get_pathname(t
);
511 /* Make a copy of the string because dirname and mkdirhier may modify the
513 strncpy(buf
, filename
, sizeof(buf
)-1);
514 buf
[sizeof(buf
)-1] = 0;
516 if (mkdirhier(dirname(buf
)) == -1)
525 if (unlink(filename
) == -1 && errno
!= ENOENT
)
535 printf(" ==> extracting: %s (symlink to %s)\n",
536 filename
, th_get_linkname(t
));
539 if (symlink(th_get_linkname(t
), filename
) == -1)
562 /* character device */
564 tar_extract_chardev(TAR
*t
, char *realname
)
567 unsigned long devmaj
, devmin
;
569 char buf
[T_BLOCKSIZE
];
585 pathname
= th_get_pathname(t
);
588 mode
= th_get_mode(t
);
589 devmaj
= th_get_devmajor(t
);
590 devmin
= th_get_devminor(t
);
592 /* Make a copy of the string because dirname and mkdirhier may modify the
594 strncpy(buf
, filename
, sizeof(buf
)-1);
595 buf
[sizeof(buf
)-1] = 0;
597 if (mkdirhier(dirname(buf
)) == -1)
607 printf(" ==> extracting: %s (character device %ld,%ld)\n",
608 filename
, devmaj
, devmin
);
610 #if !defined(WIN32) && !defined(__VMS)
611 if (mknod(filename
, mode
| S_IFCHR
,
612 compat_makedev(devmaj
, devmin
)) == -1)
641 tar_extract_blockdev(TAR
*t
, char *realname
)
644 unsigned long devmaj
, devmin
;
646 char buf
[T_BLOCKSIZE
];
661 pathname
= th_get_pathname(t
);
664 mode
= th_get_mode(t
);
665 devmaj
= th_get_devmajor(t
);
666 devmin
= th_get_devminor(t
);
668 /* Make a copy of the string because dirname and mkdirhier may modify the
670 strncpy(buf
, filename
, sizeof(buf
)-1);
671 buf
[sizeof(buf
)-1] = 0;
673 if (mkdirhier(dirname(buf
)) == -1)
683 printf(" ==> extracting: %s (block device %ld,%ld)\n",
684 filename
, devmaj
, devmin
);
686 #if !defined(WIN32) && !defined(__VMS)
687 if (mknod(filename
, mode
| S_IFBLK
,
688 compat_makedev(devmaj
, devmin
)) == -1)
717 tar_extract_dir(TAR
*t
, char *realname
)
721 char buf
[T_BLOCKSIZE
];
737 pathname
= th_get_pathname(t
);
740 mode
= th_get_mode(t
);
742 /* Make a copy of the string because dirname and mkdirhier may modify the
744 strncpy(buf
, filename
, sizeof(buf
)-1);
745 buf
[sizeof(buf
)-1] = 0;
747 if (mkdirhier(dirname(buf
)) == -1)
756 /* Strip trailing '/'...it confuses some Unixes (and BeOS)... */
757 strncpy(buf
, filename
, sizeof(buf
)-1);
758 buf
[sizeof(buf
)-1] = 0;
760 if ((len
> 0) && (buf
[len
-1] == '/'))
766 printf(" ==> extracting: %s (mode %04o, directory)\n", filename
,
770 if (mkdir(buf
) == -1)
772 if (mkdir(buf
, mode
& 07777) == -1)
776 /* There is a bug in the Borland Run time library which makes MKDIR
777 return EACCES when it should return EEXIST
778 if it is some other error besides directory exists
780 if ( errno
== EACCES
)
787 if (chmod(filename
, mode
& 07777) == -1)
801 puts(" *** using existing directory");
833 tar_extract_fifo(TAR
*t
, char *realname
)
837 char buf
[T_BLOCKSIZE
];
852 pathname
= th_get_pathname(t
);
855 mode
= th_get_mode(t
);
857 /* Make a copy of the string because dirname and mkdirhier may modify the
859 strncpy(buf
, filename
, sizeof(buf
)-1);
860 buf
[sizeof(buf
)-1] = 0;
862 if (mkdirhier(dirname(buf
)) == -1)
872 printf(" ==> extracting: %s (fifo)\n", filename
);
874 #if !defined(WIN32) && !defined(__VMS)
875 if (mkfifo(filename
, mode
& 07777) == -1)