Updated formatting of documentation plus a little reorganization.
[cmake.git] / Utilities / cmtar / extract.c
blob36599d5584acd4323d630a3a615c98d20518789f
1 /*
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
13 #include <libtarint/internal.h>
15 #include <stdio.h>
16 #include <libtar/compat.h>
17 #include <sys/types.h>
18 #include <fcntl.h>
19 #include <errno.h>
21 #if defined(_WIN32) && !defined(__CYGWIN__)
22 # ifdef _MSC_VER
23 # include <sys/utime.h>
24 # else
25 # include <utime.h>
26 # endif
27 # include <io.h>
28 # include <direct.h>
29 #else
30 # include <utime.h>
31 # ifdef HAVE_SYS_PARAM_H
32 # include <sys/param.h>
33 # endif
34 #endif
36 #ifdef STDC_HEADERS
37 # include <stdlib.h>
38 # include <string.h>
39 #endif
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
45 #ifdef HAVE_SYS_MKDEV_H
46 # include <sys/mkdev.h>
47 #endif
50 struct linkname
52 char ln_save[TAR_MAXPATHLEN];
53 char ln_real[TAR_MAXPATHLEN];
55 typedef struct linkname linkname_t;
58 static int
59 tar_set_file_perms(TAR *t, char *realname)
61 mode_t mode;
62 uid_t uid;
63 gid_t gid;
64 struct utimbuf ut;
65 char *filename;
66 char *pathname = 0;
68 if (realname)
70 filename = realname;
72 else
74 pathname = th_get_pathname(t);
75 filename = pathname;
78 mode = th_get_mode(t);
79 uid = th_get_uid(t);
80 gid = th_get_gid(t);
81 ut.modtime = ut.actime = th_get_mtime(t);
83 /* change owner/group */
84 #ifndef WIN32
85 if (geteuid() == 0)
86 #ifdef HAVE_LCHOWN
87 if (lchown(filename, uid, gid) == -1)
89 # ifdef DEBUG
90 fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
91 filename, uid, gid, strerror(errno));
92 # endif
93 #else /* ! HAVE_LCHOWN */
94 if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
96 # ifdef DEBUG
97 fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
98 filename, uid, gid, strerror(errno));
99 # endif
100 #endif /* HAVE_LCHOWN */
101 if (pathname)
103 free(pathname);
105 return -1;
108 /* change access/modification time */
109 if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
111 #ifdef DEBUG
112 perror("utime()");
113 #endif
114 if (pathname)
116 free(pathname);
118 return -1;
120 /* change permissions */
121 if (!TH_ISSYM(t) && chmod(filename, mode & 07777) == -1)
123 #ifdef DEBUG
124 perror("chmod()");
125 #endif
126 if (pathname)
128 free(pathname);
130 return -1;
133 #else /* WIN32 */
134 (void)filename;
135 (void)gid;
136 (void)uid;
137 (void)mode;
138 #endif /* WIN32 */
140 if (pathname)
142 free(pathname);
144 return 0;
148 /* switchboard */
150 tar_extract_file(TAR *t, char *realname)
152 int i;
153 linkname_t *lnp;
154 char *pathname;
156 if (t->options & TAR_NOOVERWRITE)
158 struct stat s;
160 #ifdef WIN32
161 if (stat(realname, &s) == 0 || errno != ENOENT)
162 #else
163 if (lstat(realname, &s) == 0 || errno != ENOENT)
164 #endif
166 errno = EEXIST;
167 return -1;
171 if (TH_ISDIR(t))
173 i = tar_extract_dir(t, realname);
174 if (i == 1)
175 i = 0;
177 #ifndef _WIN32
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);
188 #endif
189 else /* if (TH_ISREG(t)) */
190 i = tar_extract_regfile(t, realname);
192 if (i != 0)
193 return i;
195 i = tar_set_file_perms(t, realname);
196 if (i != 0)
197 return i;
199 lnp = (linkname_t *)calloc(1, sizeof(linkname_t));
200 if (lnp == NULL)
201 return -1;
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));
205 #ifdef DEBUG
206 printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
207 "value=\"%s\"\n", pathname, realname);
208 #endif
209 if (pathname)
211 free(pathname);
213 if (libtar_hash_add(t->h, lnp) != 0)
214 return -1;
216 return 0;
220 /* extract regular file */
222 tar_extract_regfile(TAR *t, char *realname)
224 mode_t mode;
225 size_t size;
226 uid_t uid;
227 gid_t gid;
228 int fdout;
229 ssize_t i, k;
230 char buf[T_BLOCKSIZE];
231 char *filename;
232 char *pathname = 0;
234 #ifdef DEBUG
235 printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
236 realname);
237 #endif
239 if (!TH_ISREG(t))
241 errno = EINVAL;
242 return -1;
245 if (realname)
247 filename = realname;
249 else
251 pathname = th_get_pathname(t);
252 filename = pathname;
254 mode = th_get_mode(t);
255 size = th_get_size(t);
256 uid = th_get_uid(t);
257 gid = th_get_gid(t);
259 /* Make a copy of the string because dirname and mkdirhier may modify the
260 * string */
261 strncpy(buf, filename, sizeof(buf)-1);
262 buf[sizeof(buf)-1] = 0;
264 if (mkdirhier(dirname(buf)) == -1)
266 if (pathname)
268 free(pathname);
270 return -1;
273 #ifdef DEBUG
274 printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
275 filename, mode, uid, gid, size);
276 #endif
277 fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
278 #ifdef O_BINARY
279 | O_BINARY
280 #endif
281 , 0666);
282 if (fdout == -1)
284 #ifdef DEBUG
285 perror("open()");
286 #endif
287 if (pathname)
289 free(pathname);
291 return -1;
294 #if 0
295 /* change the owner. (will only work if run as root) */
296 if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
298 #ifdef DEBUG
299 perror("fchown()");
300 #endif
301 if (pathname)
303 free(pathname);
305 return -1;
308 /* make sure the mode isn't inheritted from a file we're overwriting */
309 if (fchmod(fdout, mode & 07777) == -1)
311 #ifdef DEBUG
312 perror("fchmod()");
313 #endif
314 if (pathname)
316 free(pathname);
318 return -1;
320 #endif
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)
328 if (k != -1)
329 errno = EINVAL;
330 if (pathname)
332 free(pathname);
334 return -1;
337 /* write block to output file */
338 if (write(fdout, buf,
339 ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : (unsigned int)i)) == -1)
341 if (pathname)
343 free(pathname);
345 return -1;
349 /* close output file */
350 if (close(fdout) == -1)
352 if (pathname)
354 free(pathname);
356 return -1;
359 #ifdef DEBUG
360 printf("### done extracting %s\n", filename);
361 #endif
363 (void)filename;
364 (void)gid;
365 (void)uid;
366 (void)mode;
368 if (pathname)
370 free(pathname);
372 return 0;
376 /* skip regfile */
378 tar_skip_regfile(TAR *t)
380 ssize_t i, k;
381 size_t size;
382 char buf[T_BLOCKSIZE];
384 if (!TH_ISREG(t))
386 errno = EINVAL;
387 return -1;
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)
396 if (k != -1)
397 errno = EINVAL;
398 return -1;
402 return 0;
406 /* hardlink */
408 tar_extract_hardlink(TAR * t, char *realname)
410 char *filename;
411 char *linktgt;
412 linkname_t *lnp;
413 libtar_hashptr_t hp;
414 char buf[T_BLOCKSIZE];
415 char *pathname = 0;
417 if (!TH_ISLNK(t))
419 errno = EINVAL;
420 return -1;
423 if (realname)
425 filename = realname;
427 else
429 pathname = th_get_pathname(t);
430 filename = pathname;
433 /* Make a copy of the string because dirname and mkdirhier may modify the
434 * string */
435 strncpy(buf, filename, sizeof(buf)-1);
436 buf[sizeof(buf)-1] = 0;
438 if (mkdirhier(dirname(buf)) == -1)
440 if (pathname)
442 free(pathname);
444 return -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;
453 else
454 linktgt = th_get_linkname(t);
456 #ifdef DEBUG
457 printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
458 #endif
459 #ifndef WIN32
460 if (link(linktgt, filename) == -1)
461 #else
462 (void)linktgt;
463 #endif
465 #ifdef DEBUG
466 perror("link()");
467 #endif
468 if (pathname)
470 free(pathname);
472 return -1;
475 #ifndef WIN32
476 if (pathname)
478 free(pathname);
480 return 0;
481 #endif
485 /* symlink */
487 tar_extract_symlink(TAR *t, char *realname)
489 char *filename;
490 char buf[T_BLOCKSIZE];
491 char *pathname = 0;
493 #ifndef _WIN32
494 if (!TH_ISSYM(t))
496 errno = EINVAL;
497 return -1;
499 #endif
501 if (realname)
503 filename = realname;
505 else
507 pathname = th_get_pathname(t);
508 filename = pathname;
511 /* Make a copy of the string because dirname and mkdirhier may modify the
512 * string */
513 strncpy(buf, filename, sizeof(buf)-1);
514 buf[sizeof(buf)-1] = 0;
516 if (mkdirhier(dirname(buf)) == -1)
518 if (pathname)
520 free(pathname);
522 return -1;
525 if (unlink(filename) == -1 && errno != ENOENT)
527 if (pathname)
529 free(pathname);
531 return -1;
534 #ifdef DEBUG
535 printf(" ==> extracting: %s (symlink to %s)\n",
536 filename, th_get_linkname(t));
537 #endif
538 #ifndef WIN32
539 if (symlink(th_get_linkname(t), filename) == -1)
540 #endif
542 #ifdef DEBUG
543 perror("symlink()");
544 #endif
545 if (pathname)
547 free(pathname);
549 return -1;
552 #ifndef WIN32
553 if (pathname)
555 free(pathname);
557 return 0;
558 #endif
562 /* character device */
564 tar_extract_chardev(TAR *t, char *realname)
566 mode_t mode;
567 unsigned long devmaj, devmin;
568 char *filename;
569 char buf[T_BLOCKSIZE];
570 char *pathname = 0;
572 #ifndef _WIN32
573 if (!TH_ISCHR(t))
575 errno = EINVAL;
576 return -1;
578 #endif
579 if (realname)
581 filename = realname;
583 else
585 pathname = th_get_pathname(t);
586 filename = pathname;
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
593 * string */
594 strncpy(buf, filename, sizeof(buf)-1);
595 buf[sizeof(buf)-1] = 0;
597 if (mkdirhier(dirname(buf)) == -1)
599 if (pathname)
601 free(pathname);
603 return -1;
606 #ifdef DEBUG
607 printf(" ==> extracting: %s (character device %ld,%ld)\n",
608 filename, devmaj, devmin);
609 #endif
610 #if !defined(WIN32) && !defined(__VMS)
611 if (mknod(filename, mode | S_IFCHR,
612 compat_makedev(devmaj, devmin)) == -1)
613 #else
614 (void)devmin;
615 (void)devmaj;
616 (void)mode;
617 #endif
619 #ifdef DEBUG
620 perror("mknod()");
621 #endif
622 if (pathname)
624 free(pathname);
626 return -1;
629 #ifndef WIN32
630 if (pathname)
632 free(pathname);
634 return 0;
635 #endif
639 /* block device */
641 tar_extract_blockdev(TAR *t, char *realname)
643 mode_t mode;
644 unsigned long devmaj, devmin;
645 char *filename;
646 char buf[T_BLOCKSIZE];
647 char *pathname = 0;
649 if (!TH_ISBLK(t))
651 errno = EINVAL;
652 return -1;
655 if (realname)
657 filename = realname;
659 else
661 pathname = th_get_pathname(t);
662 filename = pathname;
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
669 * string */
670 strncpy(buf, filename, sizeof(buf)-1);
671 buf[sizeof(buf)-1] = 0;
673 if (mkdirhier(dirname(buf)) == -1)
675 if (pathname)
677 free(pathname);
679 return -1;
682 #ifdef DEBUG
683 printf(" ==> extracting: %s (block device %ld,%ld)\n",
684 filename, devmaj, devmin);
685 #endif
686 #if !defined(WIN32) && !defined(__VMS)
687 if (mknod(filename, mode | S_IFBLK,
688 compat_makedev(devmaj, devmin)) == -1)
689 #else
690 (void)devmin;
691 (void)devmaj;
692 (void)mode;
693 #endif
695 #ifdef DEBUG
696 perror("mknod()");
697 #endif
698 if (pathname)
700 free(pathname);
702 return -1;
705 #ifndef WIN32
706 if (pathname)
708 free(pathname);
710 return 0;
711 #endif
715 /* directory */
717 tar_extract_dir(TAR *t, char *realname)
719 mode_t mode;
720 char *filename;
721 char buf[T_BLOCKSIZE];
722 char *pathname = 0;
723 size_t len = 0;
725 if (!TH_ISDIR(t))
727 errno = EINVAL;
728 return -1;
731 if (realname)
733 filename = realname;
735 else
737 pathname = th_get_pathname(t);
738 filename = pathname;
740 mode = th_get_mode(t);
742 /* Make a copy of the string because dirname and mkdirhier may modify the
743 * string */
744 strncpy(buf, filename, sizeof(buf)-1);
745 buf[sizeof(buf)-1] = 0;
747 if (mkdirhier(dirname(buf)) == -1)
749 if (pathname)
751 free(pathname);
753 return -1;
756 /* Strip trailing '/'...it confuses some Unixes (and BeOS)... */
757 strncpy(buf, filename, sizeof(buf)-1);
758 buf[sizeof(buf)-1] = 0;
759 len = strlen(buf);
760 if ((len > 0) && (buf[len-1] == '/'))
762 buf[len-1] = '\0';
765 #ifdef DEBUG
766 printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
767 mode);
768 #endif
769 #ifdef WIN32
770 if (mkdir(buf) == -1)
771 #else
772 if (mkdir(buf, mode & 07777) == -1)
773 #endif
775 #ifdef __BORLANDC__
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
779 then return false */
780 if ( errno == EACCES)
782 errno = EEXIST;
784 #endif
785 if (errno == EEXIST)
787 if (chmod(filename, mode & 07777) == -1)
789 #ifdef DEBUG
790 perror("chmod()");
791 #endif
792 if (pathname)
794 free(pathname);
796 return -1;
798 else
800 #ifdef DEBUG
801 puts(" *** using existing directory");
802 #endif
803 if (pathname)
805 free(pathname);
807 return 1;
810 else
812 #ifdef DEBUG
813 perror("mkdir()");
814 #endif
815 if (pathname)
817 free(pathname);
819 return -1;
823 if (pathname)
825 free(pathname);
827 return 0;
831 /* FIFO */
833 tar_extract_fifo(TAR *t, char *realname)
835 mode_t mode;
836 char *filename;
837 char buf[T_BLOCKSIZE];
838 char *pathname = 0;
840 if (!TH_ISFIFO(t))
842 errno = EINVAL;
843 return -1;
846 if (realname)
848 filename = realname;
850 else
852 pathname = th_get_pathname(t);
853 filename = pathname;
855 mode = th_get_mode(t);
857 /* Make a copy of the string because dirname and mkdirhier may modify the
858 * string */
859 strncpy(buf, filename, sizeof(buf)-1);
860 buf[sizeof(buf)-1] = 0;
862 if (mkdirhier(dirname(buf)) == -1)
864 if (pathname)
866 free(pathname);
868 return -1;
871 #ifdef DEBUG
872 printf(" ==> extracting: %s (fifo)\n", filename);
873 #endif
874 #if !defined(WIN32) && !defined(__VMS)
875 if (mkfifo(filename, mode & 07777) == -1)
876 #else
877 (void)mode;
878 #endif
880 #ifdef DEBUG
881 perror("mkfifo()");
882 #endif
883 if (pathname)
885 free(pathname);
887 return -1;
890 #ifndef WIN32
891 if (pathname)
893 free(pathname);
895 return 0;
896 #endif