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
11 */
12
13 #include <internal.h>
14
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>
21
22 #ifdef STDC_HEADERS
23 # include <stdlib.h>
24 #endif
25
26 #ifdef HAVE_UNISTD_H
27 # include <unistd.h>
28 #endif
29
30
31 struct linkname
32 {
33 char ln_save[MAXPATHLEN];
34 char ln_real[MAXPATHLEN];
35 };
36 typedef struct linkname linkname_t;
37
38
39 static int
tar_set_file_perms(TAR * t,char * realname)40 tar_set_file_perms(TAR *t, char *realname)
41 {
42 mode_t mode;
43 uid_t uid;
44 gid_t gid;
45 struct utimbuf ut;
46 char *filename;
47
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);
53
54 /* change owner/group */
55 if (geteuid() == 0)
56 #ifdef HAVE_LCHOWN
57 if (lchown(filename, uid, gid) == -1)
58 {
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)
65 {
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;
72 }
73
74 /* change access/modification time */
75 if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
76 {
77 #ifdef DEBUG
78 perror("utime()");
79 #endif
80 return -1;
81 }
82
83 /* change permissions */
84 if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
85 {
86 #ifdef DEBUG
87 perror("chmod()");
88 #endif
89 return -1;
90 }
91
92 return 0;
93 }
94
95
96 /* switchboard */
97 int
98 tar_extract_file(TAR *t, char *realname)
99 {
100 int i;
101 linkname_t *lnp;
102
103 if (t->options & TAR_NOOVERWRITE)
104 {
105 struct stat s;
106
107 if (lstat(realname, &s) == 0 || errno != ENOENT)
108 {
109 errno = EEXIST;
110 return -1;
111 }
112 }
113
114 if (TH_ISDIR(t))
115 {
116 i = tar_extract_dir(t, realname);
117 if (i == 1)
118 i = 0;
119 }
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);
132
133 if (i != 0)
134 return i;
135
136 i = tar_set_file_perms(t, realname);
137 if (i != 0)
138 return i;
139
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;
151
152 return 0;
153 }
154
155
156 /* extract regular file */
157 int
158 tar_extract_regfile(TAR *t, char *realname)
159 {
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;
168
169 #ifdef DEBUG
170 printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
171 realname);
172 #endif
173
174 if (!TH_ISREG(t))
175 {
176 errno = EINVAL;
177 return -1;
178 }
179
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);
185
186 if (mkdirhier(dirname(filename)) == -1)
187 return -1;
188
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)
199 {
200 #ifdef DEBUG
201 perror("open()");
202 #endif
203 return -1;
204 }
205
206 #if 0
207 /* change the owner. (will only work if run as root) */
208 if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
209 {
210 #ifdef DEBUG
211 perror("fchown()");
212 #endif
213 return -1;
214 }
215
216 /* make sure the mode isn't inheritted from a file we're overwriting */
217 if (fchmod(fdout, mode & 07777) == -1)
218 {
219 #ifdef DEBUG
220 perror("fchmod()");
221 #endif
222 return -1;
223 }
224 #endif
225
226 /* extract the file */
227 for (i = size; i > 0; i -= T_BLOCKSIZE)
228 {
229 k = tar_block_read(t, buf);
230 if (k != T_BLOCKSIZE)
231 {
232 if (k != -1)
233 errno = EINVAL;
234 return -1;
235 }
236
237 /* write block to output file */
238 if (write(fdout, buf,
239 ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
240 return -1;
241 }
242
243 /* close output file */
244 if (close(fdout) == -1)
245 return -1;
246
247 #ifdef DEBUG
248 printf("### done extracting %s\n", filename);
249 #endif
250
251 return 0;
252 }
253
254
255 /* skip regfile */
256 int
257 tar_skip_regfile(TAR *t)
258 {
259 int i, k;
260 size_t size;
261 char buf[T_BLOCKSIZE];
262
263 if (!TH_ISREG(t))
264 {
265 errno = EINVAL;
266 return -1;
267 }
268
269 size = th_get_size(t);
270 for (i = size; i > 0; i -= T_BLOCKSIZE)
271 {
272 k = tar_block_read(t, buf);
273 if (k != T_BLOCKSIZE)
274 {
275 if (k != -1)
276 errno = EINVAL;
277 return -1;
278 }
279 }
280
281 return 0;
282 }
283
284
285 /* hardlink */
286 int
287 tar_extract_hardlink(TAR * t, char *realname)
288 {
289 char *filename;
290 char *linktgt = NULL;
291 linkname_t *lnp;
292 libtar_hashptr_t hp;
293
294 if (!TH_ISLNK(t))
295 {
296 errno = EINVAL;
297 return -1;
298 }
299
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)
306 {
307 lnp = (linkname_t *)libtar_hashptr_data(&hp);
308 linktgt = lnp->ln_real;
309 }
310 else
311 linktgt = th_get_linkname(t);
312
313 #ifdef DEBUG
314 printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
315 #endif
316 if (link(linktgt, filename) == -1)
317 {
318 #ifdef DEBUG
319 perror("link()");
320 #endif
321 return -1;
322 }
323
324 return 0;
325 }
326
327
328 /* symlink */
329 int
330 tar_extract_symlink(TAR *t, char *realname)
331 {
332 char *filename;
333
334 if (!TH_ISSYM(t))
335 {
336 errno = EINVAL;
337 return -1;
338 }
339
340 filename = (realname ? realname : th_get_pathname(t));
341 if (mkdirhier(dirname(filename)) == -1)
342 return -1;
343
344 if (unlink(filename) == -1 && errno != ENOENT)
345 return -1;
346
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)
352 {
353 #ifdef DEBUG
354 perror("symlink()");
355 #endif
356 return -1;
357 }
358
359 return 0;
360 }
361
362
363 /* character device */
364 int
365 tar_extract_chardev(TAR *t, char *realname)
366 {
367 mode_t mode;
368 unsigned long devmaj, devmin;
369 char *filename;
370
371 if (!TH_ISCHR(t))
372 {
373 errno = EINVAL;
374 return -1;
375 }
376
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);
381
382 if (mkdirhier(dirname(filename)) == -1)
383 return -1;
384
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)
391 {
392 #ifdef DEBUG
393 perror("mknod()");
394 #endif
395 return -1;
396 }
397
398 return 0;
399 }
400
401
402 /* block device */
403 int
404 tar_extract_blockdev(TAR *t, char *realname)
405 {
406 mode_t mode;
407 unsigned long devmaj, devmin;
408 char *filename;
409
410 if (!TH_ISBLK(t))
411 {
412 errno = EINVAL;
413 return -1;
414 }
415
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);
420
421 if (mkdirhier(dirname(filename)) == -1)
422 return -1;
423
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)
430 {
431 #ifdef DEBUG
432 perror("mknod()");
433 #endif
434 return -1;
435 }
436
437 return 0;
438 }
439
440
441 /* directory */
442 int
443 tar_extract_dir(TAR *t, char *realname)
444 {
445 mode_t mode;
446 char *filename;
447
448 if (!TH_ISDIR(t))
449 {
450 errno = EINVAL;
451 return -1;
452 }
453
454 filename = (realname ? realname : th_get_pathname(t));
455 mode = th_get_mode(t);
456
457 if (mkdirhier(dirname(filename)) == -1)
458 return -1;
459
460 #ifdef DEBUG
461 printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
462 mode);
463 #endif
464 if (mkdir(filename, mode) == -1)
465 {
466 if (errno == EEXIST)
467 {
468 if (chmod(filename, mode) == -1)
469 {
470 #ifdef DEBUG
471 perror("chmod()");
472 #endif
473 return -1;
474 }
475 else
476 {
477 #ifdef DEBUG
478 puts(" *** using existing directory");
479 #endif
480 return 1;
481 }
482 }
483 else
484 {
485 #ifdef DEBUG
486 perror("mkdir()");
487 #endif
488 return -1;
489 }
490 }
491
492 return 0;
493 }
494
495
496 /* FIFO */
497 int
498 tar_extract_fifo(TAR *t, char *realname)
499 {
500 mode_t mode;
501 char *filename;
502
503 if (!TH_ISFIFO(t))
504 {
505 errno = EINVAL;
506 return -1;
507 }
508
509 filename = (realname ? realname : th_get_pathname(t));
510 mode = th_get_mode(t);
511
512 if (mkdirhier(dirname(filename)) == -1)
513 return -1;
514
515 #ifdef DEBUG
516 printf(" ==> extracting: %s (fifo)\n", filename);
517 #endif
518 if (mkfifo(filename, mode) == -1)
519 {
520 #ifdef DEBUG
521 perror("mkfifo()");
522 #endif
523 return -1;
524 }
525
526 return 0;
527 }
528
529
530