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