1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1987-2013 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * Glenn Fowler
23  * AT&T Bell Laboratories
24  *
25  * pax file copy support
26  */
27 
28 #include "pax.h"
29 
30 /*
31  * copy files in from archive
32  */
33 
34 void
copyin(register Archive_t * ap)35 copyin(register Archive_t* ap)
36 {
37 	register File_t*	f = &ap->file;
38 
39 	deltabase(ap);
40 	while (getprologue(ap))
41 	{
42 		while (getheader(ap, f))
43 		{
44 			if (selectfile(ap, f))
45 				filein(ap, f);
46 			else
47 				fileskip(ap, f);
48 			if (ap->info)
49 				ap->info->checksum = ap->memsum;
50 			gettrailer(ap, f);
51 		}
52 		if (!getepilogue(ap))
53 			break;
54 	}
55 	deltaverify(ap);
56 }
57 
58 /*
59  * copy a single file out to the archive
60  * called by ftwalk()
61  */
62 
63 int
copyout(register Ftw_t * ftw)64 copyout(register Ftw_t* ftw)
65 {
66 	register Archive_t*	ap = state.out;
67 	register File_t*	f = &ap->file;
68 
69 	if (getfile(ap, f, ftw))
70 	{
71 		if (selectfile(ap, f))
72 		{
73 			f->fd = openin(ap, f);
74 			deltaout(NiL, ap, f);
75 		}
76 		else
77 			ftw->status = FTW_SKIP;
78 	}
79 	return 0;
80 }
81 
82 /*
83  * low level for copyout()
84  * if rfd<0 && st_size>0 then input from bread()
85  */
86 
87 void
fileout(register Archive_t * ap,register File_t * f)88 fileout(register Archive_t* ap, register File_t* f)
89 {
90 	register size_t		m;
91 	register ssize_t	n;
92 	register off_t		c;
93 	int			err;
94 	Buffer_t*		bp;
95 
96 	if (f->delta.op == DELTA_verify)
97 		ap->selected--;
98 	else if (putheader(ap, f))
99 	{
100 		if (!ap->format->putdata || !(*ap->format->putdata)(&state, ap, f, f->fd))
101 		{
102 			err = 0;
103 			c = f->st->st_size;
104 			while (c > 0)
105 			{
106 				n = m = c > state.buffersize ? state.buffersize : c;
107 				if (!err)
108 				{
109 					if (f->fd >= 0)
110 					{
111 						if ((n = read(f->fd, ap->io->next, m)) < 0 && errno == EIO)
112 						{
113 							static char*	buf;
114 
115 							if (!buf)
116 							{
117 								n = 1024 * 8;
118 								error(1, "EIO read error -- falling back to aligned reads");
119 								if (!(buf = malloc(state.buffersize + n)))
120 									nospace();
121 								buf += n - (((ssize_t)buf) & (n - 1));
122 							}
123 							if ((n = read(f->fd, buf, m)) > 0)
124 								memcpy(ap->io->next, buf, n);
125 						}
126 					}
127 					else if (bp = getbuffer(f->fd))
128 					{
129 						memcpy(ap->io->next, bp->next, m);
130 						if (f->extended && ap->convert[SECTION_CONTROL].f2a)
131 							ccmapstr(ap->convert[SECTION_CONTROL].f2a, ap->io->next, m);
132 						bp->next += m;
133 					}
134 					else if (bread(f->ap, ap->io->next, (off_t)0, (off_t)n, 1) <= 0)
135 						n = -1;
136 				}
137 				if (n <= 0)
138 				{
139 					if (n)
140 						error(ERROR_SYSTEM|2, "%s: read error", f->path);
141 					else
142 						error(2, "%s: file size changed", f->path);
143 					memzero(ap->io->next, state.buffersize);
144 					err = 1;
145 				}
146 				else
147 				{
148 					c -= n;
149 					bput(ap, n);
150 				}
151 			}
152 		}
153 		puttrailer(ap, f);
154 	}
155 	if (f->fd >= 0)
156 		closein(ap, f, f->fd);
157 }
158 
159 /*
160  * low level for copyin()
161  */
162 
163 void
filein(register Archive_t * ap,register File_t * f)164 filein(register Archive_t* ap, register File_t* f)
165 {
166 	register off_t	c;
167 	register int	n;
168 	register char*	s;
169 	int		dfd;
170 	int		wfd;
171 	long		checksum;
172 	Filter_t*	fp;
173 	Proc_t*		pp;
174 	Tv_t		t1;
175 	Tv_t		t2;
176 	struct stat	st;
177 
178 	if (f->skip)
179 		goto skip;
180 	else if (state.list)
181 	{
182 		if (fp = filter(ap, f))
183 		{
184 			for (n = 0; s = fp->argv[n]; n++)
185 			{
186 				while (*s)
187 					if (*s++ == '%' && *s == '(')
188 						break;
189 				if (*s)
190 				{
191 					s = fp->argv[n];
192 					listprintf(state.tmp.str, ap, f, s);
193 					if (!(fp->argv[n] = sfstruse(state.tmp.str)))
194 						nospace();
195 					break;
196 				}
197 			}
198 			pp = procopen(*fp->argv, fp->argv, NiL, NiL, PROC_WRITE);
199 			if (s)
200 				fp->argv[n] = s;
201 			if (!pp)
202 			{
203 				error(2, "%s: %s: cannot execute filter %s", ap->name, f->path, *fp->argv);
204 				goto skip;
205 			}
206 			if (!ap->format->getdata || !(*ap->format->getdata)(&state, ap, f, pp->wfd))
207 			{
208 				checksum = 0;
209 				for (c = f->st->st_size; c > 0; c -= n)
210 				{
211 					n = (c > state.buffersize) ? state.buffersize : c;
212 					if (!(s = bget(ap, n, NiL)))
213 					{
214 						error(ERROR_SYSTEM|2, "%s: read error", f->name);
215 						break;
216 					}
217 					if (write(pp->wfd, s, n) != n)
218 					{
219 						error(ERROR_SYSTEM|2, "%s: write error", f->name);
220 						break;
221 					}
222 					if (ap->format->checksum)
223 						checksum = (*ap->format->checksum)(&state, ap, f, s, n, checksum);
224 				}
225 			}
226 			if (ap->format->checksum && checksum != f->checksum)
227 				error(1, "%s: %s checksum error (0x%08x != 0x%08x)", f->name, ap->format->name, checksum, f->checksum);
228 			/*
229 			 * explicitly ignore exit status
230 			 */
231 
232 			procclose(pp);
233 			return;
234 		}
235 		listentry(f);
236 		goto skip;
237 	}
238 	else switch (f->delta.op)
239 	{
240 	case DELTA_create:
241 		if (f->delta.base)
242 			error(3, "%s: base archive mismatch [%s#%d]", f->name, __FILE__, __LINE__);
243 		if (ap->delta->format->flags & PSEUDO)
244 			goto regular;
245 		if ((wfd = openout(ap, f)) < 0)
246 			goto skip;
247 		else
248 			paxdelta(NiL, ap, f, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_OUTPUT|DELTA_COUNT, wfd, DELTA_DEL|DELTA_BIO|DELTA_SIZE, ap, f->st->st_size, 0);
249 		break;
250 	case DELTA_update:
251 		if (!f->delta.base || (unsigned long)f->delta.base->mtime.tv_sec >= (unsigned long)f->st->st_mtime)
252 			error(3, "%s: base archive mismatch [%s#%d]", f->name, __FILE__, __LINE__);
253 		c = f->st->st_size;
254 		if ((wfd = openout(ap, f)) < 0)
255 			goto skip;
256 		if (state.ordered)
257 		{
258 			if (!f->delta.base->uncompressed)
259 				paxdelta(NiL, ap, f, DELTA_SRC|DELTA_BIO|DELTA_SIZE, ap->delta->base, f->delta.base->size, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_OUTPUT|DELTA_COUNT, wfd, DELTA_DEL|DELTA_BIO|DELTA_SIZE, ap, c, 0);
260 			else if (!paxdelta(NiL, ap, f, DELTA_DEL|DELTA_BIO|DELTA_SIZE, ap->delta->base, f->delta.base->size, DELTA_TAR|DELTA_TEMP|DELTA_OUTPUT, &dfd, 0))
261 				paxdelta(NiL, ap, f, DELTA_SRC|DELTA_FD|DELTA_SIZE|DELTA_FREE, dfd, f->delta.base->uncompressed, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_OUTPUT|DELTA_COUNT, wfd, DELTA_DEL|DELTA_BIO|DELTA_SIZE, ap, c, 0);
262 		}
263 		else if (!f->delta.base->uncompressed)
264 			paxdelta(NiL, ap, f, DELTA_SRC|DELTA_FD|DELTA_OFFSET|DELTA_SIZE, ap->delta->base->io->fd, f->delta.base->offset, f->delta.base->size, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_OUTPUT|DELTA_COUNT, wfd, DELTA_DEL|DELTA_BIO|DELTA_SIZE, ap, c, 0);
265 		else if (!paxdelta(NiL, ap, f, DELTA_DEL|DELTA_FD|DELTA_OFFSET|DELTA_SIZE, ap->delta->base->io->fd, f->delta.base->offset, f->delta.base->size, DELTA_TAR|DELTA_TEMP|DELTA_OUTPUT, &dfd, 0))
266 			paxdelta(NiL, ap, f, DELTA_SRC|DELTA_FD|DELTA_SIZE|DELTA_FREE, dfd, f->delta.base->uncompressed, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_OUTPUT|DELTA_COUNT, wfd, DELTA_DEL|DELTA_BIO|DELTA_SIZE, ap, c, 0);
267 		break;
268 	case DELTA_verify:
269 		if (!f->delta.base || f->delta.base->mtime.tv_sec != f->st->st_mtime)
270 			error(3, "%s: base archive mismatch [%s#%d]", f->name, __FILE__, __LINE__);
271 		if ((*state.statf)(f->name, &st))
272 			error(2, "%s: not copied from base archive", f->name);
273 		else if (st.st_size != f->delta.base->size || state.modtime && tvcmp(tvmtime(&t1, &st), tvmtime(&t2, f->st)))
274 			error(1, "%s: changed from base archive", f->name);
275 		break;
276 	case DELTA_delete:
277 		if (!f->delta.base)
278 			error(3, "%s: base archive mismatch [%s#%d]", f->name, __FILE__, __LINE__);
279 		/*FALLTHROUGH*/
280 	default:
281 	regular:
282 		wfd = openout(ap, f);
283 		if (wfd >= 0)
284 		{
285 			holeinit(wfd);
286 			if (!ap->format->getdata || !(*ap->format->getdata)(&state, ap, f, wfd))
287 			{
288 				checksum = 0;
289 				for (c = f->st->st_size; c > 0; c -= n)
290 				{
291 					n = (c > state.buffersize) ? state.buffersize : c;
292 					if (!(s = bget(ap, n, NiL)))
293 					{
294 						error(ERROR_SYSTEM|2, "%s: read error", f->name);
295 						break;
296 					}
297 					if (holewrite(wfd, s, n) != n)
298 					{
299 						error(ERROR_SYSTEM|2, "%s: write error", f->name);
300 						break;
301 					}
302 					if (ap->format->checksum)
303 						checksum = (*ap->format->checksum)(&state, ap, f, s, n, checksum);
304 				}
305 			}
306 			holedone(wfd);
307 			closeout(ap, f, wfd);
308 			setfile(ap, f);
309 			if (ap->format->checksum && checksum != f->checksum)
310 				error(1, "%s: %s checksum error (0x%08x != 0x%08x)", f->name, ap->format->name, checksum, f->checksum);
311 		}
312 		else if (ap->format->getdata)
313 			(*ap->format->getdata)(&state, ap, f, wfd);
314 		else
315 		{
316 			if (wfd < -1)
317 				listentry(f);
318 			goto skip;
319 		}
320 		break;
321 	}
322 	listentry(f);
323 	return;
324  skip:
325 	fileskip(ap, f);
326 }
327 
328 /*
329  * skip over archive member f file data
330  */
331 
332 void
fileskip(register Archive_t * ap,register File_t * f)333 fileskip(register Archive_t* ap, register File_t* f)
334 {
335 	Member_t*	d;
336 	off_t		n;
337 
338 	if (ap->delta && (d = (Member_t*)hashget(ap->delta->tab, f->name)))
339 		d->info->delta.op = DELTA_delete;
340 	if ((!ap->format->getdata || !(*ap->format->getdata)(&state, ap, f, -1)) && ((n = f->st->st_size) > 0 && f->type == X_IFREG || (n = f->datasize)) && bread(ap, NiL, (off_t)0, n, 1) < 0)
341 		error(ERROR_SYSTEM|2, "%s: skip error", f->name);
342 }
343 
344 /*
345  * single file copyin() and copyout() smashed together
346  * called by ftwalk()
347  */
348 
349 int
copyinout(Ftw_t * ftw)350 copyinout(Ftw_t* ftw)
351 {
352 	register File_t*	f = &state.out->file;
353 	register char*		s;
354 	register off_t		c;
355 	register ssize_t	n;
356 	register int		rfd;
357 	register int		wfd;
358 
359 	if (getfile(state.out, f, ftw) && selectfile(state.out, f))
360 	{
361 		s = f->name;
362 		f->name = stash(&state.out->path.copy, NiL, state.pwdlen + f->namesize);
363 		strcpy(stpcpy(f->name, state.pwd), s + (*s == '/'));
364 		if ((wfd = openout(state.out, f)) >= 0)
365 		{
366 			if ((rfd = openin(state.out, f)) >= 0)
367 			{
368 #if defined(SEEK_DATA) && defined(SEEK_HOLE)
369 				off_t		data;
370 				off_t		hole;
371 				int		more;
372 
373 				data = 0;
374 				more = 1;
375 				while (more)
376 				{
377 					if ((hole = lseek(rfd, data, SEEK_HOLE)) < data)
378 					{
379 						hole = lseek(rfd, 0, SEEK_END);
380 						more = 0;
381 					}
382 					while ((c = hole - data) > 0)
383 					{
384 						if (c > state.buffersize)
385 							c = state.buffersize;
386 						if (lseek(rfd, data, SEEK_SET) != data || (n = read(rfd, state.tmp.buffer, (size_t)c)) <= 0)
387 						{
388 							error(ERROR_SYSTEM|2, "%s: read error", f->name);
389 							more = 0;
390 							break;
391 						}
392 						if (lseek(wfd, data, SEEK_SET) != data || write(wfd, state.tmp.buffer, n) != n)
393 						{
394 							error(ERROR_SYSTEM|2, "%s: write error", f->name);
395 							more = 0;
396 							break;
397 						}
398 						state.out->io->count += n;
399 						data += n;
400 					}
401 					if (!more)
402 						break;
403 					if ((data = lseek(rfd, hole, SEEK_DATA)) < hole)
404 					{
405 						if ((data = lseek(rfd, -1, SEEK_END)) < 0 || (data + 1) > hole && (lseek(wfd, data, SEEK_SET) != data || write(wfd, "", 1) != 1))
406 							error(ERROR_SYSTEM|2, "%s: write error", f->name);
407 						state.out->io->count += 1;
408 						break;
409 					}
410 				}
411 #else
412 				holeinit(wfd);
413 				for (c = f->st->st_size; c > 0; c -= n)
414 				{
415 					if ((n = read(rfd, state.tmp.buffer, (size_t)((c > state.buffersize) ? state.buffersize : c))) <= 0)
416 					{
417 						error(ERROR_SYSTEM|2, "%s: read error", f->name);
418 						break;
419 					}
420 					if (holewrite(wfd, state.tmp.buffer, n) != n)
421 					{
422 						error(ERROR_SYSTEM|2, "%s: write error", f->name);
423 						break;
424 					}
425 					state.out->io->count += n;
426 				}
427 				holedone(wfd);
428 #endif
429 				closeout(state.out, f, wfd);
430 				closein(state.out, f, rfd);
431 				setfile(state.out, f);
432 				listentry(f);
433 			}
434 			else
435 				closeout(state.out, f, wfd);
436 		}
437 		else if (wfd != -1)
438 			listentry(f);
439 	}
440 	return 0;
441 }
442 
443 /*
444  * compare ft1 and ft2 for ftwalk() sort
445  */
446 
447 int
cmpftw(Ftw_t * ft1,Ftw_t * ft2)448 cmpftw(Ftw_t* ft1, Ftw_t* ft2)
449 {
450 	return strcoll(ft1->name, ft2->name);
451 }
452 
453 /*
454  * skip to the next unquoted occurrence of d in s
455  */
456 
457 static char*
skip(register char * s,register int d)458 skip(register char* s, register int d)
459 {
460 	register int	c;
461 	register int	q;
462 
463 	q = 0;
464 	while (c = *s++)
465 		if (c == q)
466 			q = 0;
467 		else if (c == '\\')
468 		{
469 			if (*s)
470 				s++;
471 		}
472 		else if (!q)
473 		{
474 			if (c == d)
475 				return s - 1;
476 			else if (c == '"' || c == '\'')
477 				q = c;
478 		}
479 	return 0;
480 }
481 
482 /*
483  * copy files out using copyfile
484  */
485 
486 typedef int (*Ftw_cmp_t)(Ftw_t*, Ftw_t*);
487 
488 void
copy(register Archive_t * ap,register int (* copyfile)(Ftw_t *))489 copy(register Archive_t* ap, register int (*copyfile)(Ftw_t*))
490 {
491 	register char*	s;
492 	register char*	t;
493 	register char*	v;
494 	register int	c;
495 	unsigned long	flags;
496 	char*		mode;
497 	char*		mtime;
498 
499 	if (ap)
500 	{
501 		deltabase(ap);
502 		if (ap->delta && ap->delta->format != ap->expected && ap->expected)
503 			error(3, "%s: archive format %s does not match requested format %s", ap->name, ap->delta->format->name, ap->expected->name);
504 		if (state.append || state.update)
505 		{
506 			ap->format = ap->delta->format;
507 			if (!(ap->format->flags & APPEND))
508 				error(3, "%s: archive format %s does support append/update", ap->name, ap->format->name);
509 			if (state.update)
510 				ap->update = ap->delta->tab;
511 			ap->delta = 0;
512 			ap->parent = 0;
513 			ap->volume--;
514 		}
515 		putprologue(ap, state.append || state.update);
516 	}
517 	if (state.files)
518 		ftwalk((char*)state.files, copyfile, state.ftwflags|FTW_MULTIPLE, state.exact ? (Ftw_cmp_t)0 : cmpftw);
519 	else
520 	{
521 		sfopen(sfstdin, NiL, "rt");
522 		sfset(sfstdin, SF_SHARE, 0);
523 		mode = state.mode;
524 		mtime = state.mtime;
525 		for (;;)
526 		{
527 			if (s = state.peekfile)
528 			{
529 				state.peekfile = 0;
530 				c = state.peeklen;
531 			}
532 			else if (!(s = sfgetr(sfstdin, '\n', 1)))
533 				break;
534 			else
535 				c = sfvalue(sfstdin) - 1;
536 			sfwrite(state.tmp.lst, s, c);
537 			if (!(s = sfstruse(state.tmp.lst)))
538 				nospace();
539 			flags = state.ftwflags;
540 			if (state.filter.line)
541 			{
542 				if (!(c = *s++))
543 					continue;
544 				state.filter.options = s;
545 				if (!(s = skip(s, c)))
546 					continue;
547 				*s++ = 0;
548 				state.filter.command = s;
549 				if (!(s = skip(s, c)))
550 					continue;
551 				*s++ = 0;
552 				state.filter.path = s;
553 				if (!(s = skip(s, c)))
554 					state.filter.name = state.filter.path;
555 				else
556 				{
557 					*s++ = 0;
558 					state.filter.name = s;
559 					if (s = skip(s, c))
560 						*s = 0;
561 				}
562 				s = state.filter.options;
563 				for (;;)
564 				{
565 					if (t = strchr(s, ','))
566 						*t = 0;
567 					if (v = strchr(s, '='))
568 					{
569 						*v++ = 0;
570 						c = strtol(v, NiL, 0);
571 					}
572 					else
573 						c = 1;
574 					if (s[0] == 'n' && s[1] == 'o')
575 					{
576 						s += 2;
577 						c = !c;
578 					}
579 					if (streq(s, "logical") || streq(s, "physical"))
580 					{
581 						if (s[0] == 'p')
582 							c = !c;
583 						if (c)
584 							flags &= ~(FTW_META|FTW_PHYSICAL);
585 						else
586 						{
587 							flags &= ~(FTW_META);
588 							flags |= FTW_PHYSICAL;
589 						}
590 					}
591 					else if (streq(s, "mode"))
592 						state.mode = v;
593 					else if (streq(s, "mtime"))
594 						state.mtime = v;
595 					if (!t)
596 						break;
597 					s = t + 1;
598 				}
599 				s = state.filter.path;
600 				state.filter.line = *state.filter.name ? 2 : 1;
601 			}
602 			c = *s ? ftwalk(s, copyfile, flags, NiL) : 0;
603 			state.mode = mode;
604 			state.mtime = mtime;
605 			if (c)
606 			{
607 				error(2, "%s: not completely copied", s);
608 				break;
609 			}
610 		}
611 	}
612 	if (ap)
613 	{
614 		deltadelete(ap);
615 		putepilogue(ap);
616 	}
617 }
618