libtar-1.2.11 tarball sources, taken from Debian's orig tar
[libtar.git] / lib / extract.c
blob6bbb801320a7cd565a139b54d9e44d9664f93fbd
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 <internal.h>
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>
22 #ifdef STDC_HEADERS
23 # include <stdlib.h>
24 #endif
26 #ifdef HAVE_UNISTD_H
27 # include <unistd.h>
28 #endif
31 struct linkname
33 char ln_save[MAXPATHLEN];
34 char ln_real[MAXPATHLEN];
36 typedef struct linkname linkname_t;
39 static int
40 tar_set_file_perms(TAR *t, char *realname)
42 mode_t mode;
43 uid_t uid;
44 gid_t gid;
45 struct utimbuf ut;
46 char *filename;
48 filename = (realname ? realname : th_get_pathname(t));
49 mode = th_get_mode(t);
50 uid = th_get_uid(t);
51 gid = th_get_gid(t);
52 ut.modtime = ut.actime = th_get_mtime(t);
54 /* change owner/group */
55 if (geteuid() == 0)
56 #ifdef HAVE_LCHOWN
57 if (lchown(filename, uid, gid) == -1)
59 # ifdef DEBUG
60 fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
61 filename, uid, gid, strerror(errno));
62 # endif
63 #else /* ! HAVE_LCHOWN */
64 if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
66 # ifdef DEBUG
67 fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
68 filename, uid, gid, strerror(errno));
69 # endif
70 #endif /* HAVE_LCHOWN */
71 return -1;
74 /* change access/modification time */
75 if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
77 #ifdef DEBUG
78 perror("utime()");
79 #endif
80 return -1;
83 /* change permissions */
84 if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
86 #ifdef DEBUG
87 perror("chmod()");
88 #endif
89 return -1;
92 return 0;
96 /* switchboard */
97 int
98 tar_extract_file(TAR *t, char *realname)
100 int i;
101 linkname_t *lnp;
103 if (t->options & TAR_NOOVERWRITE)
105 struct stat s;
107 if (lstat(realname, &s) == 0 || errno != ENOENT)
109 errno = EEXIST;
110 return -1;
114 if (TH_ISDIR(t))
116 i = tar_extract_dir(t, realname);
117 if (i == 1)
118 i = 0;
120 else if (TH_ISLNK(t))
121 i = tar_extract_hardlink(t, realname);
122 else if (TH_ISSYM(t))
123 i = tar_extract_symlink(t, realname);
124 else if (TH_ISCHR(t))
125 i = tar_extract_chardev(t, realname);
126 else if (TH_ISBLK(t))
127 i = tar_extract_blockdev(t, realname);
128 else if (TH_ISFIFO(t))
129 i = tar_extract_fifo(t, realname);
130 else /* if (TH_ISREG(t)) */
131 i = tar_extract_regfile(t, realname);
133 if (i != 0)
134 return i;
136 i = tar_set_file_perms(t, realname);
137 if (i != 0)
138 return i;
140 lnp = (linkname_t *)calloc(1, sizeof(linkname_t));
141 if (lnp == NULL)
142 return -1;
143 strlcpy(lnp->ln_save, th_get_pathname(t), sizeof(lnp->ln_save));
144 strlcpy(lnp->ln_real, realname, sizeof(lnp->ln_real));
145 #ifdef DEBUG
146 printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
147 "value=\"%s\"\n", th_get_pathname(t), realname);
148 #endif
149 if (libtar_hash_add(t->h, lnp) != 0)
150 return -1;
152 return 0;
156 /* extract regular file */
158 tar_extract_regfile(TAR *t, char *realname)
160 mode_t mode;
161 size_t size;
162 uid_t uid;
163 gid_t gid;
164 int fdout;
165 int i, k;
166 char buf[T_BLOCKSIZE];
167 char *filename;
169 #ifdef DEBUG
170 printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
171 realname);
172 #endif
174 if (!TH_ISREG(t))
176 errno = EINVAL;
177 return -1;
180 filename = (realname ? realname : th_get_pathname(t));
181 mode = th_get_mode(t);
182 size = th_get_size(t);
183 uid = th_get_uid(t);
184 gid = th_get_gid(t);
186 if (mkdirhier(dirname(filename)) == -1)
187 return -1;
189 #ifdef DEBUG
190 printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
191 filename, mode, uid, gid, size);
192 #endif
193 fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
194 #ifdef O_BINARY
195 | O_BINARY
196 #endif
197 , 0666);
198 if (fdout == -1)
200 #ifdef DEBUG
201 perror("open()");
202 #endif
203 return -1;
206 #if 0
207 /* change the owner. (will only work if run as root) */
208 if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
210 #ifdef DEBUG
211 perror("fchown()");
212 #endif
213 return -1;
216 /* make sure the mode isn't inheritted from a file we're overwriting */
217 if (fchmod(fdout, mode & 07777) == -1)
219 #ifdef DEBUG
220 perror("fchmod()");
221 #endif
222 return -1;
224 #endif
226 /* extract the file */
227 for (i = size; i > 0; i -= T_BLOCKSIZE)
229 k = tar_block_read(t, buf);
230 if (k != T_BLOCKSIZE)
232 if (k != -1)
233 errno = EINVAL;
234 return -1;
237 /* write block to output file */
238 if (write(fdout, buf,
239 ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
240 return -1;
243 /* close output file */
244 if (close(fdout) == -1)
245 return -1;
247 #ifdef DEBUG
248 printf("### done extracting %s\n", filename);
249 #endif
251 return 0;
255 /* skip regfile */
257 tar_skip_regfile(TAR *t)
259 int i, k;
260 size_t size;
261 char buf[T_BLOCKSIZE];
263 if (!TH_ISREG(t))
265 errno = EINVAL;
266 return -1;
269 size = th_get_size(t);
270 for (i = size; i > 0; i -= T_BLOCKSIZE)
272 k = tar_block_read(t, buf);
273 if (k != T_BLOCKSIZE)
275 if (k != -1)
276 errno = EINVAL;
277 return -1;
281 return 0;
285 /* hardlink */
287 tar_extract_hardlink(TAR * t, char *realname)
289 char *filename;
290 char *linktgt = NULL;
291 linkname_t *lnp;
292 libtar_hashptr_t hp;
294 if (!TH_ISLNK(t))
296 errno = EINVAL;
297 return -1;
300 filename = (realname ? realname : th_get_pathname(t));
301 if (mkdirhier(dirname(filename)) == -1)
302 return -1;
303 libtar_hashptr_reset(&hp);
304 if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
305 (libtar_matchfunc_t)libtar_str_match) != 0)
307 lnp = (linkname_t *)libtar_hashptr_data(&hp);
308 linktgt = lnp->ln_real;
310 else
311 linktgt = th_get_linkname(t);
313 #ifdef DEBUG
314 printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
315 #endif
316 if (link(linktgt, filename) == -1)
318 #ifdef DEBUG
319 perror("link()");
320 #endif
321 return -1;
324 return 0;
328 /* symlink */
330 tar_extract_symlink(TAR *t, char *realname)
332 char *filename;
334 if (!TH_ISSYM(t))
336 errno = EINVAL;
337 return -1;
340 filename = (realname ? realname : th_get_pathname(t));
341 if (mkdirhier(dirname(filename)) == -1)
342 return -1;
344 if (unlink(filename) == -1 && errno != ENOENT)
345 return -1;
347 #ifdef DEBUG
348 printf(" ==> extracting: %s (symlink to %s)\n",
349 filename, th_get_linkname(t));
350 #endif
351 if (symlink(th_get_linkname(t), filename) == -1)
353 #ifdef DEBUG
354 perror("symlink()");
355 #endif
356 return -1;
359 return 0;
363 /* character device */
365 tar_extract_chardev(TAR *t, char *realname)
367 mode_t mode;
368 unsigned long devmaj, devmin;
369 char *filename;
371 if (!TH_ISCHR(t))
373 errno = EINVAL;
374 return -1;
377 filename = (realname ? realname : th_get_pathname(t));
378 mode = th_get_mode(t);
379 devmaj = th_get_devmajor(t);
380 devmin = th_get_devminor(t);
382 if (mkdirhier(dirname(filename)) == -1)
383 return -1;
385 #ifdef DEBUG
386 printf(" ==> extracting: %s (character device %ld,%ld)\n",
387 filename, devmaj, devmin);
388 #endif
389 if (mknod(filename, mode | S_IFCHR,
390 compat_makedev(devmaj, devmin)) == -1)
392 #ifdef DEBUG
393 perror("mknod()");
394 #endif
395 return -1;
398 return 0;
402 /* block device */
404 tar_extract_blockdev(TAR *t, char *realname)
406 mode_t mode;
407 unsigned long devmaj, devmin;
408 char *filename;
410 if (!TH_ISBLK(t))
412 errno = EINVAL;
413 return -1;
416 filename = (realname ? realname : th_get_pathname(t));
417 mode = th_get_mode(t);
418 devmaj = th_get_devmajor(t);
419 devmin = th_get_devminor(t);
421 if (mkdirhier(dirname(filename)) == -1)
422 return -1;
424 #ifdef DEBUG
425 printf(" ==> extracting: %s (block device %ld,%ld)\n",
426 filename, devmaj, devmin);
427 #endif
428 if (mknod(filename, mode | S_IFBLK,
429 compat_makedev(devmaj, devmin)) == -1)
431 #ifdef DEBUG
432 perror("mknod()");
433 #endif
434 return -1;
437 return 0;
441 /* directory */
443 tar_extract_dir(TAR *t, char *realname)
445 mode_t mode;
446 char *filename;
448 if (!TH_ISDIR(t))
450 errno = EINVAL;
451 return -1;
454 filename = (realname ? realname : th_get_pathname(t));
455 mode = th_get_mode(t);
457 if (mkdirhier(dirname(filename)) == -1)
458 return -1;
460 #ifdef DEBUG
461 printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
462 mode);
463 #endif
464 if (mkdir(filename, mode) == -1)
466 if (errno == EEXIST)
468 if (chmod(filename, mode) == -1)
470 #ifdef DEBUG
471 perror("chmod()");
472 #endif
473 return -1;
475 else
477 #ifdef DEBUG
478 puts(" *** using existing directory");
479 #endif
480 return 1;
483 else
485 #ifdef DEBUG
486 perror("mkdir()");
487 #endif
488 return -1;
492 return 0;
496 /* FIFO */
498 tar_extract_fifo(TAR *t, char *realname)
500 mode_t mode;
501 char *filename;
503 if (!TH_ISFIFO(t))
505 errno = EINVAL;
506 return -1;
509 filename = (realname ? realname : th_get_pathname(t));
510 mode = th_get_mode(t);
512 if (mkdirhier(dirname(filename)) == -1)
513 return -1;
515 #ifdef DEBUG
516 printf(" ==> extracting: %s (fifo)\n", filename);
517 #endif
518 if (mkfifo(filename, mode) == -1)
520 #ifdef DEBUG
521 perror("mkfifo()");
522 #endif
523 return -1;
526 return 0;