36357e7123ee9f18c4ad6ac5befd5e25ef2d69cc
[libtar.git] / lib / extract.c
blob36357e7123ee9f18c4ad6ac5befd5e25ef2d69cc
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 static int
33 tar_set_file_perms(TAR *t, char *realname)
35 mode_t mode;
36 uid_t uid;
37 gid_t gid;
38 struct utimbuf ut;
39 char *filename;
41 filename = (realname ? realname : th_get_pathname(t));
42 mode = th_get_mode(t);
43 uid = th_get_uid(t);
44 gid = th_get_gid(t);
45 ut.modtime = ut.actime = th_get_mtime(t);
47 /* change owner/group */
48 if (geteuid() == 0)
49 #ifdef HAVE_LCHOWN
50 if (lchown(filename, uid, gid) == -1)
52 # ifdef DEBUG
53 fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
54 filename, uid, gid, strerror(errno));
55 # endif
56 #else /* ! HAVE_LCHOWN */
57 if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
59 # ifdef DEBUG
60 fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
61 filename, uid, gid, strerror(errno));
62 # endif
63 #endif /* HAVE_LCHOWN */
64 return -1;
67 /* change access/modification time */
68 if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
70 #ifdef DEBUG
71 perror("utime()");
72 #endif
73 return -1;
76 /* change permissions */
77 if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
79 #ifdef DEBUG
80 perror("chmod()");
81 #endif
82 return -1;
85 return 0;
89 /* switchboard */
90 int
91 tar_extract_file(TAR *t, char *realname)
93 int i;
94 char *lnp;
95 int pathname_len;
96 int realname_len;
98 if (t->options & TAR_NOOVERWRITE)
100 struct stat s;
102 if (lstat(realname, &s) == 0 || errno != ENOENT)
104 errno = EEXIST;
105 return -1;
109 if (TH_ISDIR(t))
111 i = tar_extract_dir(t, realname);
112 if (i == 1)
113 i = 0;
115 else if (TH_ISLNK(t))
116 i = tar_extract_hardlink(t, realname);
117 else if (TH_ISSYM(t))
118 i = tar_extract_symlink(t, realname);
119 else if (TH_ISCHR(t))
120 i = tar_extract_chardev(t, realname);
121 else if (TH_ISBLK(t))
122 i = tar_extract_blockdev(t, realname);
123 else if (TH_ISFIFO(t))
124 i = tar_extract_fifo(t, realname);
125 else /* if (TH_ISREG(t)) */
126 i = tar_extract_regfile(t, realname);
128 if (i != 0)
129 return i;
131 i = tar_set_file_perms(t, realname);
132 if (i != 0)
133 return i;
135 pathname_len = strlen(th_get_pathname(t)) + 1;
136 realname_len = strlen(realname) + 1;
137 lnp = (char *)calloc(1, pathname_len + realname_len);
138 if (lnp == NULL)
139 return -1;
140 strcpy(&lnp[0], th_get_pathname(t));
141 strcpy(&lnp[pathname_len], realname);
142 #ifdef DEBUG
143 printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
144 "value=\"%s\"\n", th_get_pathname(t), realname);
145 #endif
146 if (libtar_hash_add(t->h, lnp) != 0)
147 return -1;
149 return 0;
153 /* extract regular file */
155 tar_extract_regfile(TAR *t, char *realname)
157 mode_t mode;
158 size_t size;
159 uid_t uid;
160 gid_t gid;
161 int fdout;
162 int i, k;
163 char buf[T_BLOCKSIZE];
164 char *filename;
166 #ifdef DEBUG
167 printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
168 realname);
169 #endif
171 if (!TH_ISREG(t))
173 errno = EINVAL;
174 return -1;
177 filename = (realname ? realname : th_get_pathname(t));
178 mode = th_get_mode(t);
179 size = th_get_size(t);
180 uid = th_get_uid(t);
181 gid = th_get_gid(t);
183 if (mkdirhier(dirname(filename)) == -1)
184 return -1;
186 #ifdef DEBUG
187 printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
188 filename, mode, uid, gid, size);
189 #endif
190 fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
191 #ifdef O_BINARY
192 | O_BINARY
193 #endif
194 , 0666);
195 if (fdout == -1)
197 #ifdef DEBUG
198 perror("open()");
199 #endif
200 return -1;
203 #if 0
204 /* change the owner. (will only work if run as root) */
205 if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
207 #ifdef DEBUG
208 perror("fchown()");
209 #endif
210 return -1;
213 /* make sure the mode isn't inheritted from a file we're overwriting */
214 if (fchmod(fdout, mode & 07777) == -1)
216 #ifdef DEBUG
217 perror("fchmod()");
218 #endif
219 return -1;
221 #endif
223 /* extract the file */
224 for (i = size; i > 0; i -= T_BLOCKSIZE)
226 k = tar_block_read(t, buf);
227 if (k != T_BLOCKSIZE)
229 if (k != -1)
230 errno = EINVAL;
231 return -1;
234 /* write block to output file */
235 if (write(fdout, buf,
236 ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
237 return -1;
240 /* close output file */
241 if (close(fdout) == -1)
242 return -1;
244 #ifdef DEBUG
245 printf("### done extracting %s\n", filename);
246 #endif
248 return 0;
252 /* skip regfile */
254 tar_skip_regfile(TAR *t)
256 int i, k;
257 size_t size;
258 char buf[T_BLOCKSIZE];
260 if (!TH_ISREG(t))
262 errno = EINVAL;
263 return -1;
266 size = th_get_size(t);
267 for (i = size; i > 0; i -= T_BLOCKSIZE)
269 k = tar_block_read(t, buf);
270 if (k != T_BLOCKSIZE)
272 if (k != -1)
273 errno = EINVAL;
274 return -1;
278 return 0;
282 /* hardlink */
284 tar_extract_hardlink(TAR * t, char *realname)
286 char *filename;
287 char *linktgt = NULL;
288 char *lnp;
289 libtar_hashptr_t hp;
291 if (!TH_ISLNK(t))
293 errno = EINVAL;
294 return -1;
297 filename = (realname ? realname : th_get_pathname(t));
298 if (mkdirhier(dirname(filename)) == -1)
299 return -1;
300 libtar_hashptr_reset(&hp);
301 if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
302 (libtar_matchfunc_t)libtar_str_match) != 0)
304 lnp = (char *)libtar_hashptr_data(&hp);
305 linktgt = &lnp[strlen(lnp) + 1];
307 else
308 linktgt = th_get_linkname(t);
310 #ifdef DEBUG
311 printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
312 #endif
313 if (link(linktgt, filename) == -1)
315 #ifdef DEBUG
316 perror("link()");
317 #endif
318 return -1;
321 return 0;
325 /* symlink */
327 tar_extract_symlink(TAR *t, char *realname)
329 char *filename;
331 if (!TH_ISSYM(t))
333 errno = EINVAL;
334 return -1;
337 filename = (realname ? realname : th_get_pathname(t));
338 if (mkdirhier(dirname(filename)) == -1)
339 return -1;
341 if (unlink(filename) == -1 && errno != ENOENT)
342 return -1;
344 #ifdef DEBUG
345 printf(" ==> extracting: %s (symlink to %s)\n",
346 filename, th_get_linkname(t));
347 #endif
348 if (symlink(th_get_linkname(t), filename) == -1)
350 #ifdef DEBUG
351 perror("symlink()");
352 #endif
353 return -1;
356 return 0;
360 /* character device */
362 tar_extract_chardev(TAR *t, char *realname)
364 mode_t mode;
365 unsigned long devmaj, devmin;
366 char *filename;
368 if (!TH_ISCHR(t))
370 errno = EINVAL;
371 return -1;
374 filename = (realname ? realname : th_get_pathname(t));
375 mode = th_get_mode(t);
376 devmaj = th_get_devmajor(t);
377 devmin = th_get_devminor(t);
379 if (mkdirhier(dirname(filename)) == -1)
380 return -1;
382 #ifdef DEBUG
383 printf(" ==> extracting: %s (character device %ld,%ld)\n",
384 filename, devmaj, devmin);
385 #endif
386 if (mknod(filename, mode | S_IFCHR,
387 compat_makedev(devmaj, devmin)) == -1)
389 #ifdef DEBUG
390 perror("mknod()");
391 #endif
392 return -1;
395 return 0;
399 /* block device */
401 tar_extract_blockdev(TAR *t, char *realname)
403 mode_t mode;
404 unsigned long devmaj, devmin;
405 char *filename;
407 if (!TH_ISBLK(t))
409 errno = EINVAL;
410 return -1;
413 filename = (realname ? realname : th_get_pathname(t));
414 mode = th_get_mode(t);
415 devmaj = th_get_devmajor(t);
416 devmin = th_get_devminor(t);
418 if (mkdirhier(dirname(filename)) == -1)
419 return -1;
421 #ifdef DEBUG
422 printf(" ==> extracting: %s (block device %ld,%ld)\n",
423 filename, devmaj, devmin);
424 #endif
425 if (mknod(filename, mode | S_IFBLK,
426 compat_makedev(devmaj, devmin)) == -1)
428 #ifdef DEBUG
429 perror("mknod()");
430 #endif
431 return -1;
434 return 0;
438 /* directory */
440 tar_extract_dir(TAR *t, char *realname)
442 mode_t mode;
443 char *filename;
445 if (!TH_ISDIR(t))
447 errno = EINVAL;
448 return -1;
451 filename = (realname ? realname : th_get_pathname(t));
452 mode = th_get_mode(t);
454 if (mkdirhier(dirname(filename)) == -1)
455 return -1;
457 #ifdef DEBUG
458 printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
459 mode);
460 #endif
461 if (mkdir(filename, mode) == -1)
463 if (errno == EEXIST)
465 if (chmod(filename, mode) == -1)
467 #ifdef DEBUG
468 perror("chmod()");
469 #endif
470 return -1;
472 else
474 #ifdef DEBUG
475 puts(" *** using existing directory");
476 #endif
477 return 1;
480 else
482 #ifdef DEBUG
483 perror("mkdir()");
484 #endif
485 return -1;
489 return 0;
493 /* FIFO */
495 tar_extract_fifo(TAR *t, char *realname)
497 mode_t mode;
498 char *filename;
500 if (!TH_ISFIFO(t))
502 errno = EINVAL;
503 return -1;
506 filename = (realname ? realname : th_get_pathname(t));
507 mode = th_get_mode(t);
509 if (mkdirhier(dirname(filename)) == -1)
510 return -1;
512 #ifdef DEBUG
513 printf(" ==> extracting: %s (fifo)\n", filename);
514 #endif
515 if (mkfifo(filename, mode) == -1)
517 #ifdef DEBUG
518 perror("mkfifo()");
519 #endif
520 return -1;
523 return 0;