Fix memory leak in th_get_pathname
[libtar.git] / lib / extract.c
blob6a3bd85170c6c21522101fa0d709607dd84b5a33
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 <string.h>
17 #include <sys/param.h>
18 #include <sys/types.h>
19 #include <fcntl.h>
20 #include <errno.h>
21 #include <utime.h>
23 #ifdef STDC_HEADERS
24 # include <stdlib.h>
25 #endif
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
32 struct linkname
34 char ln_save[MAXPATHLEN];
35 char ln_real[MAXPATHLEN];
37 typedef struct linkname linkname_t;
40 static int
41 tar_set_file_perms(TAR *t, char *realname)
43 mode_t mode;
44 uid_t uid;
45 gid_t gid;
46 struct utimbuf ut;
47 char *filename;
49 filename = (realname ? realname : th_get_pathname(t));
50 mode = th_get_mode(t);
51 uid = th_get_uid(t);
52 gid = th_get_gid(t);
53 ut.modtime = ut.actime = th_get_mtime(t);
55 /* change owner/group */
56 if (geteuid() == 0)
57 #ifdef HAVE_LCHOWN
58 if (lchown(filename, uid, gid) == -1)
60 # ifdef DEBUG
61 fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
62 filename, uid, gid, strerror(errno));
63 # endif
64 #else /* ! HAVE_LCHOWN */
65 if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
67 # ifdef DEBUG
68 fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
69 filename, uid, gid, strerror(errno));
70 # endif
71 #endif /* HAVE_LCHOWN */
72 return -1;
75 /* change access/modification time */
76 if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
78 #ifdef DEBUG
79 perror("utime()");
80 #endif
81 return -1;
84 /* change permissions */
85 if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
87 #ifdef DEBUG
88 perror("chmod()");
89 #endif
90 return -1;
93 return 0;
97 /* switchboard */
98 int
99 tar_extract_file(TAR *t, char *realname)
101 int i;
102 linkname_t *lnp;
104 if (t->options & TAR_NOOVERWRITE)
106 struct stat s;
108 if (lstat(realname, &s) == 0 || errno != ENOENT)
110 errno = EEXIST;
111 return -1;
115 if (TH_ISDIR(t))
117 i = tar_extract_dir(t, realname);
118 if (i == 1)
119 i = 0;
121 else if (TH_ISLNK(t))
122 i = tar_extract_hardlink(t, realname);
123 else if (TH_ISSYM(t))
124 i = tar_extract_symlink(t, realname);
125 else if (TH_ISCHR(t))
126 i = tar_extract_chardev(t, realname);
127 else if (TH_ISBLK(t))
128 i = tar_extract_blockdev(t, realname);
129 else if (TH_ISFIFO(t))
130 i = tar_extract_fifo(t, realname);
131 else /* if (TH_ISREG(t)) */
132 i = tar_extract_regfile(t, realname);
134 if (i != 0)
135 return i;
137 i = tar_set_file_perms(t, realname);
138 if (i != 0)
139 return i;
141 lnp = (linkname_t *)calloc(1, sizeof(linkname_t));
142 if (lnp == NULL)
143 return -1;
144 strlcpy(lnp->ln_save, th_get_pathname(t), sizeof(lnp->ln_save));
145 strlcpy(lnp->ln_real, realname, sizeof(lnp->ln_real));
146 #ifdef DEBUG
147 printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
148 "value=\"%s\"\n", th_get_pathname(t), realname);
149 #endif
150 if (libtar_hash_add(t->h, lnp) != 0)
151 return -1;
153 return 0;
157 /* extract regular file */
159 tar_extract_regfile(TAR *t, char *realname)
161 mode_t mode;
162 size_t size;
163 uid_t uid;
164 gid_t gid;
165 int fdout;
166 int i, k;
167 char buf[T_BLOCKSIZE];
168 char *filename;
170 #ifdef DEBUG
171 printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
172 realname);
173 #endif
175 if (!TH_ISREG(t))
177 errno = EINVAL;
178 return -1;
181 filename = (realname ? realname : th_get_pathname(t));
182 mode = th_get_mode(t);
183 size = th_get_size(t);
184 uid = th_get_uid(t);
185 gid = th_get_gid(t);
187 if (mkdirhier(dirname(filename)) == -1)
188 return -1;
190 #ifdef DEBUG
191 printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
192 filename, mode, uid, gid, size);
193 #endif
194 fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
195 #ifdef O_BINARY
196 | O_BINARY
197 #endif
198 , 0666);
199 if (fdout == -1)
201 #ifdef DEBUG
202 perror("open()");
203 #endif
204 return -1;
207 #if 0
208 /* change the owner. (will only work if run as root) */
209 if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
211 #ifdef DEBUG
212 perror("fchown()");
213 #endif
214 return -1;
217 /* make sure the mode isn't inheritted from a file we're overwriting */
218 if (fchmod(fdout, mode & 07777) == -1)
220 #ifdef DEBUG
221 perror("fchmod()");
222 #endif
223 return -1;
225 #endif
227 /* extract the file */
228 for (i = size; i > 0; i -= T_BLOCKSIZE)
230 k = tar_block_read(t, buf);
231 if (k != T_BLOCKSIZE)
233 if (k != -1)
234 errno = EINVAL;
235 return -1;
238 /* write block to output file */
239 if (write(fdout, buf,
240 ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
241 return -1;
244 /* close output file */
245 if (close(fdout) == -1)
246 return -1;
248 #ifdef DEBUG
249 printf("### done extracting %s\n", filename);
250 #endif
252 return 0;
256 /* skip regfile */
258 tar_skip_regfile(TAR *t)
260 int i, k;
261 size_t size;
262 char buf[T_BLOCKSIZE];
264 if (!TH_ISREG(t))
266 errno = EINVAL;
267 return -1;
270 size = th_get_size(t);
271 for (i = size; i > 0; i -= T_BLOCKSIZE)
273 k = tar_block_read(t, buf);
274 if (k != T_BLOCKSIZE)
276 if (k != -1)
277 errno = EINVAL;
278 return -1;
282 return 0;
286 /* hardlink */
288 tar_extract_hardlink(TAR * t, char *realname)
290 char *filename;
291 char *linktgt = NULL;
292 linkname_t *lnp;
293 libtar_hashptr_t hp;
295 if (!TH_ISLNK(t))
297 errno = EINVAL;
298 return -1;
301 filename = (realname ? realname : th_get_pathname(t));
302 if (mkdirhier(dirname(filename)) == -1)
303 return -1;
304 libtar_hashptr_reset(&hp);
305 if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
306 (libtar_matchfunc_t)libtar_str_match) != 0)
308 lnp = (linkname_t *)libtar_hashptr_data(&hp);
309 linktgt = lnp->ln_real;
311 else
312 linktgt = th_get_linkname(t);
314 #ifdef DEBUG
315 printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
316 #endif
317 if (link(linktgt, filename) == -1)
319 #ifdef DEBUG
320 perror("link()");
321 #endif
322 return -1;
325 return 0;
329 /* symlink */
331 tar_extract_symlink(TAR *t, char *realname)
333 char *filename;
335 if (!TH_ISSYM(t))
337 errno = EINVAL;
338 return -1;
341 filename = (realname ? realname : th_get_pathname(t));
342 if (mkdirhier(dirname(filename)) == -1)
343 return -1;
345 if (unlink(filename) == -1 && errno != ENOENT)
346 return -1;
348 #ifdef DEBUG
349 printf(" ==> extracting: %s (symlink to %s)\n",
350 filename, th_get_linkname(t));
351 #endif
352 if (symlink(th_get_linkname(t), filename) == -1)
354 #ifdef DEBUG
355 perror("symlink()");
356 #endif
357 return -1;
360 return 0;
364 /* character device */
366 tar_extract_chardev(TAR *t, char *realname)
368 mode_t mode;
369 unsigned long devmaj, devmin;
370 char *filename;
372 if (!TH_ISCHR(t))
374 errno = EINVAL;
375 return -1;
378 filename = (realname ? realname : th_get_pathname(t));
379 mode = th_get_mode(t);
380 devmaj = th_get_devmajor(t);
381 devmin = th_get_devminor(t);
383 if (mkdirhier(dirname(filename)) == -1)
384 return -1;
386 #ifdef DEBUG
387 printf(" ==> extracting: %s (character device %ld,%ld)\n",
388 filename, devmaj, devmin);
389 #endif
390 if (mknod(filename, mode | S_IFCHR,
391 compat_makedev(devmaj, devmin)) == -1)
393 #ifdef DEBUG
394 perror("mknod()");
395 #endif
396 return -1;
399 return 0;
403 /* block device */
405 tar_extract_blockdev(TAR *t, char *realname)
407 mode_t mode;
408 unsigned long devmaj, devmin;
409 char *filename;
411 if (!TH_ISBLK(t))
413 errno = EINVAL;
414 return -1;
417 filename = (realname ? realname : th_get_pathname(t));
418 mode = th_get_mode(t);
419 devmaj = th_get_devmajor(t);
420 devmin = th_get_devminor(t);
422 if (mkdirhier(dirname(filename)) == -1)
423 return -1;
425 #ifdef DEBUG
426 printf(" ==> extracting: %s (block device %ld,%ld)\n",
427 filename, devmaj, devmin);
428 #endif
429 if (mknod(filename, mode | S_IFBLK,
430 compat_makedev(devmaj, devmin)) == -1)
432 #ifdef DEBUG
433 perror("mknod()");
434 #endif
435 return -1;
438 return 0;
442 /* directory */
444 tar_extract_dir(TAR *t, char *realname)
446 mode_t mode;
447 char *filename;
449 if (!TH_ISDIR(t))
451 errno = EINVAL;
452 return -1;
455 filename = (realname ? realname : th_get_pathname(t));
456 mode = th_get_mode(t);
458 if (mkdirhier(dirname(filename)) == -1)
459 return -1;
461 #ifdef DEBUG
462 printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
463 mode);
464 #endif
465 if (mkdir(filename, mode) == -1)
467 if (errno == EEXIST)
469 if (chmod(filename, mode) == -1)
471 #ifdef DEBUG
472 perror("chmod()");
473 #endif
474 return -1;
476 else
478 #ifdef DEBUG
479 puts(" *** using existing directory");
480 #endif
481 return 1;
484 else
486 #ifdef DEBUG
487 perror("mkdir()");
488 #endif
489 return -1;
493 return 0;
497 /* FIFO */
499 tar_extract_fifo(TAR *t, char *realname)
501 mode_t mode;
502 char *filename;
504 if (!TH_ISFIFO(t))
506 errno = EINVAL;
507 return -1;
510 filename = (realname ? realname : th_get_pathname(t));
511 mode = th_get_mode(t);
513 if (mkdirhier(dirname(filename)) == -1)
514 return -1;
516 #ifdef DEBUG
517 printf(" ==> extracting: %s (fifo)\n", filename);
518 #endif
519 if (mkfifo(filename, mode) == -1)
521 #ifdef DEBUG
522 perror("mkfifo()");
523 #endif
524 return -1;
527 return 0;