Change th_get_size() macro to return unsigned int
[libtar.git] / lib / extract.c
blob9fc6ad5dd083fe3ff00cb6e3a8b5b6f5949d64d1
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 close(fdout);
232 return -1;
235 /* write block to output file */
236 if (write(fdout, buf,
237 ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
239 close(fdout);
240 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 char *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 = (char *)libtar_hashptr_data(&hp);
309 linktgt = &lnp[strlen(lnp) + 1];
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;