1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1987-2011 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 delta archive support
26  */
27 
28 #include "pax.h"
29 
30 #include <update.h>
31 
32 /*
33  * interpret delta header/trailer ops
34  */
35 
36 static void
getdeltaops(Archive_t * ap,File_t * f)37 getdeltaops(Archive_t* ap, File_t* f)
38 {
39 	register char*	s;
40 	register char*	e;
41 	register int	n;
42 	char		c;
43 
44 	if (state.delta2delta || (ap->format->flags & DELTAINFO))
45 		return;
46 	s = ap->delta->hdrbuf;
47 	e = s + sizeof(ap->delta->hdrbuf) - 1;
48 	n = 0;
49 	while (f->st->st_size > 0 && bread(ap, &c, (off_t)1, (off_t)1, 1) > 0)
50 	{
51 		f->st->st_size--;
52 		if (c == '\t' || c == '\n')
53 		{
54 			if (n)
55 			{
56 				*s = 0;
57 				s = ap->delta->hdrbuf;
58 				switch (n)
59 				{
60 				case DELTA_checksum:
61 					f->delta.checksum = strtoul(s, NiL, 16);
62 					break;
63 				case DELTA_index:
64 					f->delta.index = strtol(s, NiL, 0);
65 					break;
66 				case DELTA_trailer:
67 					if ((n = strtol(s, NiL, 0)) > 0)
68 					{
69 						ap->delta->epilogue = -1;
70 						ap->delta->trailer = n;
71 						f->st->st_size -= n;
72 					}
73 					break;
74 
75 					/*
76 					 * ignore unknown ops for future
77 					 */
78 				}
79 				n = 0;
80 			}
81 			if (c == '\n')
82 				break;
83 		}
84 		else if (!n) n = c;
85 		else if (s < e) *s++ = c;
86 	}
87 }
88 
89 /*
90  * get supplemental delta header info
91  */
92 
93 void
getdeltaheader(register Archive_t * ap,register File_t * f)94 getdeltaheader(register Archive_t* ap, register File_t* f)
95 {
96 	register char*	s;
97 	int		n;
98 	unsigned long	sum;
99 	Sfio_t*		sp;
100 	char		c;
101 
102 	if (!(ap->format->flags & COMPRESSED))
103 	{
104 		if (ap->delta && ap->delta->format && (ap->delta->format->variant == DELTA_94 || ap->delta->format->variant == DELTA_IGNORE && state.delta2delta))
105 		{
106 			ap->delta->index++;
107 			if (ap->delta->tab && f->name && (f->delta.base = (Member_t*)hashget(ap->delta->tab, f->name)))
108 				f->delta.base->mark = 1;
109 			if (!(ap->format->flags & DELTAINFO))
110 			{
111 				if (f->st->st_size <= 0 || bread(ap, &c, (off_t)1, (off_t)1, 1) <= 0)
112 					f->delta.op = DELTA_create;
113 				else
114 				{
115 					f->st->st_size--;
116 					f->delta.op = c;
117 					getdeltaops(ap, f);
118 					if (f->st->st_size >= 12 && (f->delta.op == DELTA_create || f->delta.op == DELTA_update))
119 					{
120 						sum = ap->memsum;
121 						s = ap->delta->hdrbuf;
122 						n = 12;
123 						if (bread(ap, s, (off_t)n, (off_t)n, 1) > 0)
124 						{
125 							if (ap->delta->format->variant == DELTA_88)
126 							{
127 								unsigned char*	u = (unsigned char*)s;
128 								int		i;
129 
130 								i = *u++;
131 								u += (i >> 3) & 07;
132 								f->uncompressed = 0;
133 								i &= 07;
134 								while (i-- > 0)
135 									f->uncompressed = f->uncompressed * 256 + *u++;
136 							}
137 							else if (sp = sfnew(NiL, s + 4, n, -1, SF_READ|SF_STRING))
138 							{
139 								f->uncompressed = sfgetu(sp);
140 								sfclose(sp);
141 							}
142 							bunread(ap, s, n);
143 						}
144 						ap->memsum = sum;
145 					}
146 				}
147 			}
148 		}
149 		else if (state.operation == (IN|OUT))
150 			f->delta.op = DELTA_pass;
151 	}
152 }
153 
154 /*
155  * get supplemental delta trailer info
156  */
157 
158 void
getdeltatrailer(register Archive_t * ap,register File_t * f)159 getdeltatrailer(register Archive_t* ap, register File_t* f)
160 {
161 	long		n;
162 	unsigned long	x;
163 
164 	if (ap->delta && !f->extended)
165 	{
166 		if (ap->delta->trailer)
167 		{
168 			f->st->st_size += ap->delta->trailer;
169 			ap->delta->trailer = 0;
170 			getdeltaops(ap, f);
171 		}
172 		if (!f->delta.base)
173 		{
174 			if (ap->parent && ap->parent != ap && f->delta.op != DELTA_create)
175 				error(2, "%s: %s: corrupt archive: not in base archive %s", ap->name, f->name, ap->parent->name);
176 		}
177 		else
178 		{
179 			x = (ap->format->flags & SUM) ? f->delta.base->info->checksum : ap->memsum;
180 			if ((f->delta.checksum ^ x) & 0xffffffff)
181 				error(2, "%s: %s: corrupt archive: checksum mismatch -- expected %08lx, got %08lx", ap->name, f->name, f->delta.checksum & 0xffffffff, x & 0xffffffff);
182 		}
183 		if (n = f->delta.index - ap->delta->index)
184 		{
185 			if (n > 0)
186 				error(2, "%s: %s: corrupt archive: %d missing file%s", ap->name, f->name, n, n == 1 ? "" : "s");
187 			else
188 				error(2, "%s: %s: corrupt archive: delta index out of sync by %d file%s", ap->name, f->name, -n, -n == 1 ? "" : "s");
189 			ap->delta->index = n;
190 		}
191 	}
192 }
193 
194 /*
195  * initialize delta header output info
196  */
197 
198 void
setdeltaheader(register Archive_t * ap,register File_t * f)199 setdeltaheader(register Archive_t* ap, register File_t* f)
200 {
201 	register char*	s;
202 	register int	n;
203 
204 	if (f->delta.op && ap->delta)
205 	{
206 		ap->delta->index++;
207 		if (ap->format->flags & STANDARD)
208 		{
209 			switch (f->delta.op)
210 			{
211 			case DELTA_create:
212 				s = "create";
213 				break;
214 			case DELTA_delete:
215 				s = "delete";
216 				break;
217 			case DELTA_pass:
218 				s = "pass";
219 				break;
220 			case DELTA_update:
221 				s = "update";
222 				break;
223 			case DELTA_verify:
224 				s = "verify";
225 				break;
226 			}
227 			putkey(ap, ap->tmp.extended, &options[OPT_delta_op], s, 0);
228 			putkey(ap, ap->tmp.extended, &options[OPT_uncompressed], NiL, f->uncompressed);
229 			putkey(ap, ap->tmp.extended, &options[OPT_delta_index], NiL, ap->delta->index);
230 			if (ap->delta->tab && (f->delta.base = (Member_t*)hashget(ap->delta->tab, f->name)))
231 				putkey(ap, ap->tmp.extended, &options[OPT_delta_checksum], NiL, f->delta.base->info->checksum & 0xffffffff);
232 		}
233 		else
234 		{
235 			s = ap->delta->hdrbuf;
236 			n = sfsprintf(s, sizeof(ap->delta->hdrbuf), "%c%c%d\t%c%d\n", f->delta.op, DELTA_index, ap->delta->index, DELTA_trailer, DELTA_TRAILER);
237 			ap->delta->hdr = s + n;
238 			n += DELTA_TRAILER;
239 			f->st->st_size += n;
240 			ap->memsum = 0;
241 			ap->sum = 1;
242 		}
243 	}
244 }
245 
246 /*
247  * output supplementary delta header info
248  */
249 
250 void
putdeltaheader(register Archive_t * ap,register File_t * f)251 putdeltaheader(register Archive_t* ap, register File_t* f)
252 {
253 	int	n;
254 
255 	if (f->delta.op && ap->delta && (n = ap->delta->hdr - ap->delta->hdrbuf))
256 	{
257 		bwrite(ap, ap->delta->hdrbuf, n);
258 		n += DELTA_TRAILER;
259 		f->st->st_size -= n;
260 		ap->delta->hdr = ap->delta->hdrbuf;
261 	}
262 }
263 
264 /*
265  * output supplementary delta trailer info
266  */
267 
268 void
putdeltatrailer(register Archive_t * ap,register File_t * f)269 putdeltatrailer(register Archive_t* ap, register File_t* f)
270 {
271 	register char*	s;
272 	register int	n;
273 
274 	if (f->delta.op && ap->delta)
275 	{
276 		if (!(ap->format->flags & STANDARD))
277 		{
278 			ap->sum = 0;
279 			s = ap->delta->hdrbuf;
280 			n = sfsprintf(s, sizeof(ap->delta->hdrbuf), "%c%08X\n", DELTA_checksum, ap->memsum);
281 			if (n != DELTA_TRAILER)
282 				error(PANIC, "delta trailer size %d shoulda been %d", n, DELTA_TRAILER);
283 			bwrite(ap, s, n);
284 		}
285 	}
286 }
287 
288 /*
289  * initialize delta tables
290  */
291 
292 void
initdelta(Archive_t * ap,Format_t * dp)293 initdelta(Archive_t* ap, Format_t* dp)
294 {
295 	if (!ap->delta && !(ap->delta = newof(0, Delta_t, 1, 0)))
296 		nospace();
297 	if (!ap->delta->tab && !(ap->delta->tab = hashalloc(NiL, HASH_set, HASH_ALLOCATE, HASH_name, "delta", 0)))
298 		nospace();
299 	ap->delta->format = dp;
300 }
301 
302 /*
303  * get delta base archive info
304  */
305 
306 void
deltabase(register Archive_t * ap)307 deltabase(register Archive_t* ap)
308 {
309 	register Archive_t*	bp;
310 	Format_t*		fp;
311 	struct stat		st;
312 
313 	if (!ap->delta || ap->delta->initialized)
314 		return;
315 	ap->delta->initialized = 1;
316 	if (!(bp = ap->delta->base))
317 		bp = ap->delta->base = initarchive("/dev/null", O_RDONLY);
318 	binit(bp);
319 	bp->parent = ap;
320 	if ((bp->io->mode & O_ACCMODE) != O_WRONLY)
321 		bp->sum++;
322 	if ((bp->io->fd = open(bp->name, bp->io->mode|O_BINARY)) < 0 || fstat(bp->io->fd, &st))
323 		error(ERROR_SYSTEM|3, "%s: %s: cannot open base archive", ap->name, bp->name);
324 	if (S_ISREG(st.st_mode) && st.st_size > 0)
325 	{
326 		bp->io->seekable = 1;
327 		bp->io->size = st.st_size;
328 	}
329 	else
330 		bp->io->seekable = 0;
331 	if (st.st_size)
332 	{
333 		fp = bp->expected;
334 		bp->expected = 0;
335 		if (state.ordered)
336 		{
337 			if (!getprologue(bp))
338 				error(3, "%s: %s: base archive is empty", ap->name, bp->name);
339 			bp->sum--;
340 			bp->checksum = memsum(bp->io->next, bcount(bp), 0L);
341 		}
342 		else
343 		{
344 			if (!state.append && !state.update && lseek(bp->io->fd, (off_t)0, SEEK_SET) != 0)
345 				error(ERROR_SYSTEM|3, "%s: %s: base archive must be seekable", ap->name, bp->name);
346 			copyin(bp);
347 			bp->size = bp->io->offset + bp->io->count;
348 		}
349 		bp->expected = fp;
350 		bp->checksum &= 0xffffffff;
351 	}
352 	if (state.append || state.update)
353 		ap->delta->format = st.st_size ? bp->format : ap->format;
354 	else if (!ap->delta->format)
355 	{
356 		ap->delta->format = getformat(FMT_DELTA, 1);
357 		ap->delta->compress = !st.st_size;
358 	}
359 }
360 
361 /*
362  * verify untouched base files
363  */
364 
365 void
deltaverify(Archive_t * ap)366 deltaverify(Archive_t* ap)
367 {
368 	register int		wfd;
369 	register Member_t*	d;
370 	register off_t		c;
371 	register off_t		n;
372 	Hash_position_t*	pos;
373 
374 	if (!state.delta.update && !state.list && ap->delta && ap->delta->base != ap && (pos = hashscan(ap->delta->tab, 0)))
375 	{
376 		message((-2, "verify untouched base files"));
377 		while (hashnext(pos))
378 		{
379 			d = (Member_t*)pos->bucket->value;
380 			message((-1, "%s: mark=%d", d->info->name, d->mark));
381 			if (!d->mark && selectfile(ap, d->info) && (wfd = openout(ap, d->info)) >= 0)
382 			{
383 				ap->entries++;
384 				if (!d->uncompressed)
385 				{
386 					if (!state.ordered && lseek(ap->delta->base->io->fd, d->offset, SEEK_SET) != d->offset)
387 						error(ERROR_SYSTEM|3, "%s: base archive seek error", ap->delta->base->name);
388 					holeinit(wfd);
389 					for (c = d->info->st->st_size; c > 0; c -= n)
390 					{
391 						n = (c > state.buffersize) ? state.buffersize : c;
392 						if ((n = state.ordered ? bread(ap, state.tmp.buffer, n, n, 1) : read(ap->delta->base->io->fd, state.tmp.buffer, n)) <= 0)
393 						{
394 							error(ERROR_SYSTEM|2, "%s: %s: read error", ap->delta->base->name, d->info->name);
395 							break;
396 						}
397 						else ap->io->count += n;
398 						if (holewrite(wfd, state.tmp.buffer, n) != n)
399 						{
400 							error(ERROR_SYSTEM|2, "%s: write error", d->info->name);
401 							break;
402 						}
403 						ap->io->count += n;
404 					}
405 					holedone(wfd);
406 					closeout(ap, d->info, wfd);
407 					setfile(ap, d->info);
408 					listentry(d->info);
409 				}
410 				else if (state.ordered) paxdelta(NiL, ap, d->info, DELTA_DEL|DELTA_BIO|DELTA_SIZE, ap->delta->base, d->size, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_OUTPUT|DELTA_COUNT|DELTA_LIST, wfd, 0);
411 				else paxdelta(NiL, ap, d->info, DELTA_DEL|DELTA_FD|DELTA_OFFSET|DELTA_SIZE, ap->delta->base->io->fd, d->offset, d->size, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_OUTPUT|DELTA_COUNT|DELTA_LIST, wfd, 0);
412 			}
413 		}
414 		hashdone(pos);
415 	}
416 }
417 
418 /*
419  * output prefix dirs to the archive first
420  */
421 
422 static void
deltaprefix(Archive_t * ip,Archive_t * op,register Member_t * d)423 deltaprefix(Archive_t* ip, Archive_t* op, register Member_t* d)
424 {
425 	register char*		s;
426 	register Member_t*	m;
427 
428 	d->mark = 1;
429 	if (s = strrchr(d->info->path, '/'))
430 	{
431 		*s = 0;
432 		if (!(m = (Member_t*)hashget(ip->delta->tab, d->info->name)))
433 		{
434 			if (!(m = newof(0, Member_t, 1, 0)))
435 				nospace();
436 			m->mark = 1;
437 			hashput(ip->delta->tab, 0, m);
438 		}
439 		else if (!m->mark)
440 			deltaprefix(ip, op, m);
441 		*s = '/';
442 	}
443 	d->info->fd = -1;
444 	fileout(op, d->info);
445 }
446 
447 /*
448  * delta file if necessary and copy out
449  */
450 
451 void
deltaout(Archive_t * ip,Archive_t * op,register File_t * f)452 deltaout(Archive_t* ip, Archive_t* op, register File_t* f)
453 {
454 	register Member_t*	d;
455 	int			dfd;
456 	int			skip;
457 
458 	skip = !!ip;
459 	f->delta.same = 0;
460 	if (d = op->delta && op->delta->tab && f->name ? (Member_t*)hashget(op->delta->tab, f->name) : (Member_t*)0)
461 		d->mark = 1;
462 	if (op->delta && (op->delta->format->flags & DELTAIO))
463 	{
464 		if (f->type == X_IFREG && f->linktype == NOLINK && (!d || f->st->st_mtime != d->mtime.tv_sec || (f->st->st_mode & X_IPERM) != (d->mode & X_IPERM)))
465 		{
466 			if (f->ordered)
467 			{
468 				f->ordered = 0;
469 				fileout(op, f);
470 				return;
471 			}
472 			if (d)
473 			{
474 				f->delta.op = DELTA_update;
475 				f->st->st_dev = d->dev;
476 				f->st->st_ino = d->ino;
477 				message((-2, "delta: delta: file=%s offset=%ld size=%ld expand=%d", f->name, d->offset, d->size, d->uncompressed));
478 				if (state.ordered)
479 				{
480 					if (!d->uncompressed)
481 						paxdelta(ip, op, f, DELTA_SRC|DELTA_BIO|DELTA_SIZE, op->delta->base, d->size, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_SIZE, f->fd, f->st->st_size, DELTA_DEL|DELTA_TEMP|DELTA_OUTPUT, &f->fd, 0);
482 					else if (!paxdelta(ip, op, d->info, DELTA_DEL|DELTA_BIO|DELTA_SIZE, op->delta->base, d->size, DELTA_TAR|DELTA_TEMP|DELTA_OUTPUT, &dfd, 0))
483 						paxdelta(ip, op, f, DELTA_SRC|DELTA_FD|DELTA_SIZE|DELTA_FREE, dfd, d->uncompressed, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_SIZE, f->fd, f->st->st_size, DELTA_DEL|DELTA_TEMP|DELTA_OUTPUT, &f->fd, 0);
484 				}
485 				else if (!d->uncompressed)
486 					paxdelta(ip, op, f, DELTA_SRC|DELTA_FD|DELTA_OFFSET|DELTA_SIZE, op->delta->base->io->fd, d->offset, d->size, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_SIZE, f->fd, f->st->st_size, DELTA_DEL|DELTA_TEMP|DELTA_OUTPUT, &f->fd, 0);
487 				else if (!paxdelta(ip, op, d->info, DELTA_DEL|DELTA_FD|DELTA_OFFSET|DELTA_SIZE, op->delta->base->io->fd, d->offset, d->size, DELTA_TAR|DELTA_TEMP|DELTA_OUTPUT, &dfd, 0))
488 					paxdelta(ip, op, f, DELTA_SRC|DELTA_FD|DELTA_SIZE|DELTA_FREE, dfd, d->uncompressed, DELTA_TAR|DELTA_FD|DELTA_SIZE|DELTA_FREE, f->fd, f->st->st_size, DELTA_DEL|DELTA_TEMP|DELTA_OUTPUT, &f->fd, 0);
489 			}
490 			else
491 			{
492 				f->delta.op = DELTA_create;
493 				message((-2, "delta: create: file=%s", f->name));
494 				paxdelta(ip, op, f, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_SIZE, f->fd, f->st->st_size, DELTA_DEL|DELTA_TEMP|DELTA_OUTPUT, &f->fd, 0);
495 			}
496 			skip = 0;
497 		}
498 		else
499 		{
500 			f->delta.op = (d && (f->type == X_IFREG || f->type == X_IFDIR)) ? DELTA_update : DELTA_create;
501 			message((-2, "delta: %s: file=%s", f->delta.op == DELTA_update ? "update" : "create", f->name));
502 			if (skip)
503 			{
504 				skip = 0;
505 				fileskip(ip, f);
506 			}
507 			f->st->st_size = 0;
508 			if (f->delta.op == DELTA_update && f->type != X_IFREG && f->st->st_mode == d->mode)
509 				f->st->st_mtime = d->mtime.tv_sec;
510 		}
511 	}
512 	if (!d || !f->delta.same && d->mtime.tv_sec != f->st->st_mtime)
513 	{
514 		register char*	s;
515 
516 		if (ip && ip->delta && ip->delta->tab && f->name && (s = strrchr(f->name, '/')))
517 		{
518 			*s = 0;
519 			if ((d = (Member_t*)hashget(ip->delta->tab, f->name)) && !d->mark)
520 				deltaprefix(ip, op, d);
521 			*s = '/';
522 		}
523 		fileout(op, f);
524 	}
525 	else if (f->fd >= 0)
526 		close(f->fd);
527 	else if (skip)
528 		fileskip(ip, f);
529 }
530 
531 /*
532  * copy file from input to output archive
533  */
534 
535 static void
deltacopy(Archive_t * ip,Archive_t * op,register File_t * f)536 deltacopy(Archive_t* ip, Archive_t* op, register File_t* f)
537 {
538 	if (f->delta.base)
539 	{
540 		f->st->st_size = f->delta.base->size;
541 		if (f->delta.base->uncompressed)
542 		{
543 			if (state.ordered) paxdelta(ip, NiL, f->delta.base->info, DELTA_DEL|DELTA_BIO|DELTA_SIZE, ip->delta->base, f->delta.base->size, DELTA_TAR|DELTA_TEMP|DELTA_OUTPUT, &f->fd, 0);
544 			else paxdelta(ip, NiL, f->delta.base->info, DELTA_DEL|DELTA_FD|DELTA_OFFSET|DELTA_SIZE, ip->delta->base->io->fd, f->delta.base->offset, f->delta.base->size, DELTA_TAR|DELTA_TEMP|DELTA_OUTPUT, &f->fd, 0);
545 		}
546 		else if (state.ordered)
547 		{
548 			f->ap = ip;
549 			f->fd = -1;
550 			f->ordered = 1;
551 		}
552 		else if ((f->fd = dup(ip->delta->base->io->fd)) < 0)
553 			error(ERROR_SYSTEM|3, "%s: cannot reopen", ip->delta->base->name);
554 		else if (lseek(f->fd, f->delta.base->offset, SEEK_SET) < 0)
555 			error(ERROR_SYSTEM|3, "%s: base archive seek error", ip->delta->base->name);
556 	}
557 	deltaout(ip, op, f);
558 }
559 
560 /*
561  * copy the delta base archive delete entries
562  */
563 
564 void
deltadelete(register Archive_t * ap)565 deltadelete(register Archive_t* ap)
566 {
567 	register File_t*	f;
568 	register Member_t*	d;
569 	Hash_position_t*	pos;
570 
571 	if (!state.ordered && ap->delta && ap->delta->tab)
572 	{
573 		if (pos = hashscan(ap->delta->tab, 0))
574 		{
575 			message((-2, "copy the base delete entries"));
576 			f = &ap->file;
577 			while (hashnext(pos))
578 			{
579 				d = (Member_t*)pos->bucket->value;
580 				if (!d->mark && (!d->info || !d->info->ro))
581 				{
582 					ap->selected++;
583 					initfile(ap, f, f->st, pos->bucket->name, X_IFREG|X_IRUSR|X_IWUSR|X_IRGRP|X_IROTH);
584 					f->delta.op = DELTA_delete;
585 					if (d->info && d->info->st)
586 					{
587 						f->st->st_mode = d->info->st->st_mode;
588 						f->st->st_gid = d->info->st->st_gid;
589 						f->st->st_uid = d->info->st->st_uid;
590 						f->st->st_mtime = d->info->st->st_mtime;
591 					}
592 					else
593 					{
594 						f->st->st_gid = state.uid;
595 						f->st->st_uid = state.gid;
596 						f->st->st_mtime = NOW;
597 					}
598 					putheader(ap, f);
599 					puttrailer(ap, f);
600 				}
601 			}
602 			hashdone(pos);
603 		}
604 	}
605 }
606 
607 /*
608  * update file deltas from archive and output to archive
609  */
610 
611 void
deltapass(Archive_t * ip,Archive_t * op)612 deltapass(Archive_t* ip, Archive_t* op)
613 {
614 	register File_t*	f;
615 	register off_t		c;
616 	register ssize_t	n;
617 	Member_t*		d;
618 	Member_t*		h;
619 	char*			p;
620 	Filter_t*		fp;
621 	Hash_position_t*	pos;
622 
623 	if (state.delta2delta != 2)
624 		state.delta2delta = 0;
625 	message((-1, "delta PASS%s", state.delta2delta ? " delta2delta" : ""));
626 	deltabase(ip);
627 	deltabase(op);
628 	putprologue(op, 0);
629 	f = &ip->file;
630 	while (getprologue(ip))
631 	{
632 		while (getheader(ip, f))
633 		{
634 			f->fd = -1;
635 			f->ap = ip;
636 			if (f->ro)
637 				fileskip(ip, f);
638 			else if (state.delta2delta)
639 			{
640 				if (validout(op, f) && selectfile(op, f))
641 					fileout(op, f);
642 				else
643 					fileskip(ip, f);
644 			}
645 			else switch (f->delta.op)
646 			{
647 			case DELTA_create:
648 				if (f->type != X_IFREG || f->linktype != NOLINK)
649 				{
650 					f->st->st_size = 0;
651 					goto pass;
652 				}
653 				if (f->delta.base)
654 					error(3, "%s: base archive mismatch [%s#%d]", f->name, __FILE__, __LINE__);
655 				if (validout(op, f) && selectfile(op, f))
656 				{
657 					paxdelta(ip, op, f, DELTA_TAR|DELTA_TEMP|DELTA_OUTPUT, &f->fd, DELTA_DEL|DELTA_BIO|DELTA_SIZE, ip, f->st->st_size, 0);
658 					if (op->delta && (op->delta->format->flags & DELTAIO))
659 					{
660 						f->delta.op = DELTA_create;
661 						paxdelta(ip, op, f, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_SIZE, f->fd, f->st->st_size, DELTA_DEL|DELTA_TEMP|DELTA_OUTPUT, &f->fd, 0);
662 					}
663 					else f->delta.op = 0;
664 					deltaout(ip, op, f);
665 				}
666 				else fileskip(ip, f);
667 				break;
668 			case DELTA_pass:
669 				if (validout(op, f) && selectfile(op, f))
670 				{
671 					if (ip->delta && (ip->delta->format->flags & DELTAIO))
672 					{
673 						f->delta.op = DELTA_create;
674 						paxdelta(ip, op, f, DELTA_DEL|DELTA_BIO|DELTA_SIZE, ip, f->st->st_size, DELTA_TAR|DELTA_TEMP|DELTA_OUTPUT, &f->fd, 0);
675 					}
676 					else if (fp = filter(op, f))
677 					{
678 						int		wfd;
679 
680 						static char*	tmp;
681 
682 						if (!tmp)
683 							tmp = pathtemp(NiL, 0, NiL, error_info.id, NiL);
684 						if ((wfd = open(tmp, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, S_IRUSR)) < 0)
685 						{
686 							error(2, "%s: cannot create filter temporary %s", f->path, tmp);
687 							fileskip(ip, f);
688 							break;
689 						}
690 						holeinit(wfd);
691 						for (c = f->st->st_size; c > 0; c -= state.buffersize)
692 						{
693 							n = c > state.buffersize ? state.buffersize : c;
694 							if (bread(ip, state.tmp.buffer, n, n, 1) <= 0)
695 							{
696 								error(ERROR_SYSTEM|2, "%s: read error", f->name);
697 								break;
698 							}
699 							if (holewrite(wfd, state.tmp.buffer, n) != n)
700 							{
701 								error(ERROR_SYSTEM|2, "%s: filter temporary write error to %s", f->name, tmp);
702 								break;
703 							}
704 						}
705 						holedone(wfd);
706 						close(wfd);
707 						p = f->path;
708 						f->path = tmp;
709 						f->fd = apply(op, f, fp);
710 						if (remove(f->path))
711 							error(1, "%s: cannot remove filter temporary", p, f->path);
712 						f->path = p;
713 						f->delta.op = 0;
714 					}
715 					else f->delta.op = 0;
716 					deltaout(ip, op, f);
717 				}
718 				else fileskip(ip, f);
719 				break;
720 			case DELTA_delete:
721 				if (!f->delta.base)
722 					error(3, "%s: base archive mismatch [%s#%d]", f->name, __FILE__, __LINE__);
723 				if (ip->delta && (d = (Member_t*)hashget(ip->delta->tab, f->name)))
724 					d->info->delta.op = DELTA_delete;
725 				break;
726 			case DELTA_update:
727 				if (!f->delta.base)
728 					error(3, "%s: base archive mismatch [%s#%d]", f->name, __FILE__, __LINE__);
729 				if (validout(op, f) && selectfile(op, f))
730 				{
731 					if (state.ordered) paxdelta(ip, op, f, DELTA_SRC|DELTA_BIO|DELTA_SIZE, ip->delta->base, f->delta.base->size, DELTA_TAR|DELTA_TEMP|DELTA_OUTPUT, &f->fd, DELTA_DEL|DELTA_BIO|DELTA_SIZE, ip, f->st->st_size, 0);
732 					else paxdelta(ip, op, f, DELTA_SRC|DELTA_FD|DELTA_OFFSET|DELTA_SIZE, ip->delta->base->io->fd, f->delta.base->offset, f->delta.base->size, DELTA_TAR|DELTA_TEMP|DELTA_OUTPUT, &f->fd, DELTA_DEL|DELTA_BIO|DELTA_SIZE, ip, f->st->st_size, 0);
733 					if (op->delta && (op->delta->format->flags & DELTAIO))
734 					{
735 						f->delta.op = DELTA_create;
736 						paxdelta(ip, op, f, DELTA_TAR|DELTA_FD|DELTA_FREE|DELTA_SIZE, f->fd, f->st->st_size, DELTA_DEL|DELTA_TEMP|DELTA_OUTPUT, &f->fd, 0);
737 					}
738 					else f->delta.op = 0;
739 					deltaout(ip, op, f);
740 				}
741 				else fileskip(ip, f);
742 				break;
743 			case DELTA_verify:
744 				if (!f->delta.base || f->delta.base->mtime.tv_sec != f->st->st_mtime)
745 					error(3, "%s: base archive mismatch [%s#%d]", f->name, __FILE__, __LINE__);
746 			pass:
747 				if (validout(op, f) && selectfile(op, f))
748 				{
749 					f->delta.op = 0;
750 					deltacopy(ip, op, f);
751 				}
752 				else fileskip(ip, f);
753 				break;
754 			default:
755 				error(3, "%s: %s: not a delta archive (op=%d)", ip->name, f->name, f->delta.op);
756 				break;
757 			}
758 			gettrailer(ip, f);
759 		}
760 		if (!getepilogue(ip))
761 			break;
762 	}
763 	if (ip->delta && ip->delta->tab)
764 	{
765 		/*
766 		 * copy the non-empty untouched base hard links first
767 		 */
768 
769 		if (pos = hashscan(ip->delta->tab, 0))
770 		{
771 			message((-2, "copy non-empty untouched base hard links"));
772 			while (hashnext(pos))
773 			{
774 				d = (Member_t*)pos->bucket->value;
775 				if (!d->mark && d->info->linktype != HARDLINK && d->info->st->st_size > 0 && selectfile(op, d->info))
776 				{
777 					d->mark = 1;
778 					deltacopy(ip, op, d->info);
779 				}
780 			}
781 			hashdone(pos);
782 		}
783 
784 		/*
785 		 * copy the remaining untouched base files and deletes
786 		 */
787 
788 		if (pos = hashscan(ip->delta->tab, 0))
789 		{
790 			message((-2, "copy remaining untouched base files"));
791 			while (hashnext(pos))
792 			{
793 				d = (Member_t*)pos->bucket->value;
794 				if (!d->mark && selectfile(op, d->info))
795 				{
796 					d->mark = 1;
797 					if (d->info->linktype == HARDLINK)
798 					{
799 						if (!(h = (Member_t*)hashget(ip->delta->tab, d->info->linkpath)))
800 							error(1, "%s: %s: %s: hard link not in base archive", ip->name, d->info->name, d->info->linkpath);
801 						else if (!h->mark || h->info->delta.op == DELTA_delete)
802 						{
803 							h->info->name = d->info->name;
804 							d = h;
805 						}
806 					}
807 					deltacopy(ip, op, d->info);
808 				}
809 			}
810 			hashdone(pos);
811 		}
812 	}
813 	deltadelete(op);
814 	putepilogue(op);
815 	op->volume = 0;
816 	op->selected = op->entries;
817 }
818 
819 /*
820  * set delta info from pseudo file
821  */
822 
823 void
deltaset(register Archive_t * ap,char * s)824 deltaset(register Archive_t* ap, char* s)
825 {
826 	Format_t*	dp;
827 	char*		t;
828 	int		type;
829 
830 	dp = 0;
831 	if ((type = *++s) != TYPE_COMPRESS && type != TYPE_DELTA)
832 		error(3, "type %c encoding not supported", *s);
833 	if (*++s == INFO_SEP)
834 	{
835 		if (t = strchr(++s, INFO_SEP))
836 			*t++ = 0;
837 		if (*s)
838 		{
839 			if (isdigit(*s))
840 				s = sfprints("delta%s", s);
841 			dp = getformat(s, 1);
842 		}
843 
844 		/*
845 		 * [<INFO_SEP>[<OP>]<VAL>]* may appear here
846 		 */
847 
848 		while ((s = t) && *s != INFO_SEP)
849 		{
850 			if (t = strchr(s, INFO_SEP))
851 				*t++ = 0;
852 			switch (*s++)
853 			{
854 			case INFO_ORDERED:
855 				ap->ordered = 1;
856 				break;
857 			}
858 		}
859 	}
860 	if (!dp)
861 		dp = getformat(FMT_DELTA, 1);
862 	initdelta(ap, dp);
863 	ap->delta->compress = type == TYPE_COMPRESS;
864 	ap->checkdelta = 0;
865 }
866 
867 /*
868  * check for delta pseudo file
869  */
870 
871 int
deltacheck(register Archive_t * ap,register File_t * f)872 deltacheck(register Archive_t* ap, register File_t* f)
873 {
874 	register char*		s;
875 	register Archive_t*	bp;
876 	register char*		t;
877 	off_t			size;
878 	unsigned long		checksum;
879 
880 	if (!f || !f->st->st_size && !f->st->st_dev && !f->st->st_ino && !(f->st->st_mode & (X_IRWXU|X_IRWXG|X_IRWXO)) && strmatch(f->name, INFO_MATCH))
881 	{
882 		if (ap->checkdelta)
883 		{
884 			ap->checkdelta = 0;
885 			if (f)
886 			{
887 				s = f->name;
888 				size = f->st->st_mtime;
889 				checksum = (DELTA_LO(f->st->st_gid) << 16) | DELTA_LO(f->st->st_uid);
890 				if (streq(s, "DELTA!!!"))
891 					initdelta(ap, getformat("delta88", 1));
892 				else if (*s++ == INFO_SEP)
893 				{
894 					if (strneq(s, ID, IDLEN) && (t = strchr(s, INFO_SEP)))
895 						deltaset(ap, t);
896 					else
897 					{
898 						if (t = strchr(s += 2, INFO_SEP))
899 							*t = 0;
900 						error(1, "unknown %s header ignored", s);
901 						return 0;
902 					}
903 				}
904 			}
905 			else if (ap->delta)
906 			{
907 				size = ap->delta->size;
908 				checksum = ap->delta->checksum;
909 			}
910 			if (ap->delta && ap->delta->format)
911 			{
912 				if (!ap->delta->compress && ap->parent)
913 					error(3, "%s: %s: base archive cannot be a delta", ap->parent->name, ap->name);
914 				if (bp = ap->delta->base)
915 				{
916 					if (ap->delta->format->variant == DELTA_88)
917 						bp->checksum = bp->old.checksum;
918 					message((-5, "checkdelta: %s size=%I*d:%I*d checksum=%08x:%08x", ap->delta->format->name, sizeof(size), size, sizeof(bp->size), bp->size, checksum, bp->checksum));
919 					if (!ap->delta->compress)
920 					{
921 						if (!ap->ordered)
922 						{
923 							if (state.ordered)
924 								error(3, "%s: delta archive not ordered", ap->name);
925 							if (bp->size != size)
926 								error(3, "%s: %s: base archive size mismatch -- expected %I*u, got %I*u", ap->name, bp->name, sizeof(size), size, sizeof(bp->size), bp->size);
927 						}
928 						if ((bp->checksum ^ checksum) & 0xffffffff)
929 							error(1, "%s: %s: base archive checksum mismatch -- expected %08lx, got %08lx", ap->name, bp->name, checksum & 0xffffffff, bp->checksum & 0xffffffff);
930 					}
931 				}
932 				else if (!ap->delta->compress)
933 				{
934 					error(state.list ? 1 : 3, "%s: base archive must be specified", ap->name);
935 					deltabase(ap);
936 					ap->delta->compress = 1;
937 				}
938 				if (ap->sum <= 0)
939 					ap->sum++;
940 				return 1;
941 			}
942 			if (f)
943 				error(1, "%s: %s: unknown control header treated as regular file", ap->name, f->name);
944 		}
945 		else if (f && *f->name == INFO_SEP && strneq(f->name + 1, ID, IDLEN) && *(f->name + IDLEN + 1) == INFO_SEP)
946 		{
947 			getdeltaheader(ap, f);
948 			setinfo(ap, f);
949 			return 1;
950 		}
951 	}
952 	if (f && ap->checkdelta && ap->delta)
953 		ap->delta->format = getformat(FMT_PATCH, 1);
954 	return 0;
955 }
956 
957 #include <vdelta.h>
958 
959 typedef struct
960 {
961 	Vddisc_t	vd;
962 	Archive_t*	ap;
963 	off_t		base;
964 	Archive_t*	bp;
965 	off_t		offset;
966 	int*		pfd;
967 	int		fd;
968 	int		op;
969 } Vdio_t;
970 
971 #define Vdoff_t		long
972 
973 /*
974  * delta discipline read
975  */
976 
977 static int
delread(void * buf,int n,Vdoff_t off,Vddisc_t * vd)978 delread(void* buf, int n, Vdoff_t off, Vddisc_t* vd)
979 {
980 	register Vdio_t*	dp = (Vdio_t*)vd;
981 	register Vdoff_t	diff;
982 
983 	message((-6, "delread: op=%o buf=%p n=%d off=%I*d nxt=%I*d", dp->op, buf, n, sizeof(off), off, sizeof(dp->offset), dp->offset));
984 	if (diff = off - dp->offset)
985 	{
986 		dp->offset = off;
987 		if (dp->op & DELTA_BIO)
988 		{
989 			if (bseek(dp->bp, diff, SEEK_CUR, 0) < 0)
990 				error(PANIC, "BIO seek: have=%I*d need=%I*d", sizeof(dp->offset), dp->offset, sizeof(off), off);
991 		}
992 		else
993 		{
994 			off += dp->base;
995 			if (lseek(dp->fd, off, SEEK_SET) != off)
996 			{
997 				message((-8, "delread: fd=%d n=%d off=%I*d: seek error", dp->fd, n, sizeof(off), off));
998 				return -1;
999 			}
1000 		}
1001 	}
1002 	if (n > (dp->vd.size - dp->offset))
1003 		n = dp->vd.size - dp->offset;
1004 	if (n <= 0)
1005 	{
1006 		message((-8, "delread: fd=%d n=%d siz=%I*d off=%I*d: seek error", dp->fd, n, sizeof(dp->vd.size), dp->vd.size, sizeof(dp->offset), dp->offset));
1007 		return 0;
1008 	}
1009 	n = (dp->op & DELTA_BIO) ? bread(dp->bp, buf, (off_t)0, (off_t)n, 1) : read(dp->fd, buf, n);
1010 	if (n > 0)
1011 		dp->offset += n;
1012 	return n;
1013 }
1014 
1015 /*
1016  * delta discipline write
1017  */
1018 
1019 static int
delwrite(void * buf,int n,Vdoff_t off,Vddisc_t * vd)1020 delwrite(void* buf, int n, Vdoff_t off, Vddisc_t* vd)
1021 {
1022 	register Vdio_t*	dp = (Vdio_t*)vd;
1023 	Buffer_t*		bp;
1024 	ssize_t			k;
1025 
1026 	message((-6, "delwrite: op=%o buf=%p n=%d off=%I*d", dp->op, buf, n, sizeof(off), off));
1027 	if (dp->op & DELTA_BIO)
1028 	{
1029 		bwrite(dp->bp, buf, n);
1030 		return n;
1031 	}
1032 	if (dp->op & DELTA_HOLE)
1033 		return holewrite(dp->fd, buf, n);
1034 	if (bp = getbuffer(dp->fd))
1035 	{
1036 		if (bp->next + n < bp->past)
1037 		{
1038 			memcpy(bp->next, buf, n);
1039 			bp->next += n;
1040 			return n;
1041 		}
1042 		if ((dp->fd = open(state.tmp.file, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, S_IRUSR)) < 0)
1043 			error(3, "%s: cannot create delta temporary file", state.tmp.file);
1044 		k = bp->next - bp->base;
1045 		if (k > 0 && write(dp->fd, bp->base, k) != k)
1046 			return -1;
1047 	}
1048 	return write(dp->fd, buf, n);
1049 }
1050 
1051 /*
1052  * delta/update algorithm wrapper
1053  */
1054 
1055 int
paxdelta(Archive_t * ip,Archive_t * ap,File_t * f,int op,...)1056 paxdelta(Archive_t* ip, Archive_t* ap, File_t* f, int op, ...)
1057 {
1058 	register Vdio_t*	dp;
1059 	va_list			vp;
1060 	ssize_t			n;
1061 	int			bufferclash = 0;
1062 	int			hole = 0;
1063 	Format_t*		fp;
1064 	Buffer_t*		bp;
1065 	Sfio_t*			mp = 0;
1066 	Vdio_t*			gen = 0;
1067 	Vdio_t			data[3];
1068 
1069 #if DEBUG
1070 	static const char*	dataname[] = { "src", "tar", "del", "HUH" };
1071 #endif
1072 
1073 	va_start(vp, op);
1074 #if 0
1075 	if (ap && ap->delta && !ap->delta->format)
1076 		ap->delta->format = getformat(FMT_DELTA, 1);
1077 	if (ip && ip->delta && !ip->delta->format)
1078 		ip->delta->format = getformat(FMT_DELTA, 1);
1079 #else
1080 	if (ap && ap->delta)
1081 	{
1082 		if (!ap->delta->format)
1083 			ap->delta->format = getformat(FMT_DELTA, 1);
1084 		fp = ap->delta->format;
1085 	}
1086 	if (ip && ip->delta)
1087 	{
1088 		if (!ip->delta->format)
1089 			ip->delta->format = getformat(FMT_DELTA, 1);
1090 		fp = ip->delta->format;
1091 	}
1092 #endif
1093 #if DEBUG
1094 	if (error_info.trace <= -5) mp = sfstropen();
1095 #endif
1096 	memzero(data, sizeof(data));
1097 	while (op)
1098 	{
1099 		dp = &data[op & DELTA_DATA];
1100 		dp->ap = ap && ap->delta ? ap : ip;
1101 #if DEBUG
1102 		if (mp) sfprintf(mp, " %s [", dataname[op & DELTA_DATA]);
1103 #endif
1104 		if (op & DELTA_BIO)
1105 			dp->bp = va_arg(vp, Archive_t*);
1106 		else if (op & DELTA_FD)
1107 		{
1108 			if ((dp->fd = va_arg(vp, int)) == -1)
1109 			{
1110 				op &= ~(DELTA_FD|DELTA_FREE);
1111 				op |= DELTA_BIO;
1112 				dp->bp = ip ? ip : ap;
1113 			}
1114 			else if (bp = getbuffer(dp->fd))
1115 			{
1116 				op &= ~(DELTA_FD|DELTA_FREE);
1117 				op |= DELTA_BUFFER;
1118 				dp->vd.data = bp->base;
1119 				bufferclash = 1;
1120 			}
1121 #if DEBUG
1122 			else if (mp) sfprintf(mp, " fd=%d", dp->fd);
1123 #endif
1124 		}
1125 		else if (op & DELTA_TEMP)
1126 		{
1127 			dp->pfd = va_arg(vp, int*);
1128 			op |= DELTA_FD;
1129 			op &= ~DELTA_FREE;
1130 #if DEBUG
1131 			if (mp) sfprintf(mp, " TEMP");
1132 #endif
1133 		}
1134 		else if (op & DELTA_BUFFER)
1135 		{
1136 			dp->vd.data = va_arg(vp, char*);
1137 #if DEBUG
1138 			if (mp) sfprintf(mp, " buffer=%p", dp->vd.data);
1139 #endif
1140 		}
1141 		if (op & DELTA_OFFSET)
1142 		{
1143 			dp->base = va_arg(vp, off_t);
1144 #if DEBUG
1145 			if (mp) sfprintf(mp, " offset=%I*d", sizeof(dp->base), dp->base);
1146 #endif
1147 		}
1148 		if (op & DELTA_SIZE)
1149 		{
1150 			dp->vd.size = va_arg(vp, off_t);
1151 #if DEBUG
1152 			if (mp) sfprintf(mp, " size=%I*d", sizeof(dp->vd.size), dp->vd.size);
1153 #endif
1154 		}
1155 		if ((op & (DELTA_BIO|DELTA_OUTPUT)) == DELTA_BIO && dp->vd.size > 0 && dp->vd.size <= state.buffersize && (dp->vd.data = bget(dp->bp, dp->vd.size, NiL)))
1156 		{
1157 			op &= ~(DELTA_BIO|DELTA_FREE);
1158 			op |= DELTA_BUFFER;
1159 #if DEBUG
1160 			if (mp) sfprintf(mp, " buffer=%p", dp->vd.data);
1161 #endif
1162 		}
1163 		if (op & (DELTA_BIO|DELTA_FD))
1164 		{
1165 			if (op & DELTA_OUTPUT)
1166 			{
1167 				dp->vd.writef = delwrite;
1168 				if (!hole && (op & (DELTA_FD|DELTA_TEMP)) == DELTA_FD)
1169 				{
1170 					hole = 1;
1171 					op |= DELTA_HOLE;
1172 					holeinit(dp->fd);
1173 				}
1174 			}
1175 			else dp->vd.readf = delread;
1176 			if ((op & (DELTA_FD|DELTA_TEMP)) == DELTA_FD && dp->base && lseek(dp->fd, dp->base, SEEK_SET) != dp->base)
1177 				error(3, "%s: cannot seek delta", f->name);
1178 #if DEBUG
1179 			if (mp && (op & DELTA_BIO)) sfprintf(mp, " bio=%s", dp->bp->name);
1180 #endif
1181 		}
1182 		if (op & DELTA_OUTPUT)
1183 		{
1184 			if (gen) error(PANIC, "paxdelta(): more than one DELTA_OUTPUT");
1185 			gen = dp;
1186 #if DEBUG
1187 			if (mp) sfprintf(mp, " OUTPUT");
1188 #endif
1189 		}
1190 #if DEBUG
1191 		if (mp && (op & DELTA_COUNT)) sfprintf(mp, " COUNT");
1192 #endif
1193 #if DEBUG
1194 		if (mp) sfprintf(mp, " ]");
1195 #endif
1196 		dp->op = op;
1197 		op = va_arg(vp, int);
1198 	}
1199 	va_end(vp);
1200 	if (!gen)
1201 		error(PANIC, "paxdelta(): no DELTA_OUTPUT");
1202 #if 0
1203 	fp = (gen == &data[DELTA_DEL]) ? ap->delta->format : ip->delta->format;
1204 #endif
1205 	if (gen->pfd)
1206 	{
1207 		if (!(state.test & 0000020) && fp->variant != DELTA_88 && (state.buffer[bufferclash ? (state.delta.bufferindex = !state.delta.bufferindex) : state.delta.bufferindex].base || (state.buffer[state.delta.bufferindex].base = newof(0, char, state.delta.buffersize, 0)) || (state.delta.buffersize >>= 1) && (state.buffer[state.delta.bufferindex].base = newof(0, char, state.delta.buffersize, 0))))
1208 		{
1209 			gen->fd = setbuffer(state.delta.bufferindex);
1210 			bp = getbuffer(gen->fd);
1211 			bp->next = bp->base;
1212 			bp->past = bp->base + state.delta.buffersize;
1213 		}
1214 		else if ((gen->fd = open(state.tmp.file, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, S_IRUSR)) < 0)
1215 			error(3, "%s: cannot create delta temporary file", state.tmp.file);
1216 	}
1217 	switch (fp->variant)
1218 	{
1219 	case DELTA_88:
1220 	case DELTA_PATCH:
1221 		/*
1222 		 * force the new interface into the old
1223 		 * not the most efficient way but at least
1224 		 * the rest of the code is clean
1225 		 */
1226 
1227 		{
1228 			static char*	tmp;
1229 
1230 			if (gen == &data[DELTA_DEL])
1231 			{
1232 				if (!(data[DELTA_DEL].op & DELTA_FD))
1233 					error(PANIC, "paxdelta: %s %s must be DELTA_TEMP or DELTA_FD", fp->name, dataname[DELTA_DEL]);
1234 				for (op = 0; op < elementsof(data); op++)
1235 				{
1236 					if (op == DELTA_DEL) /*NOP*/;
1237 					else if (!data[op].vd.size)
1238 					{
1239 						data[op].vd.data = state.tmp.buffer;
1240 						data[op].op &= ~DELTA_FREE;
1241 					}
1242 					else switch (data[op].op & (DELTA_BIO|DELTA_FD|DELTA_OUTPUT))
1243 					{
1244 					case DELTA_BIO:
1245 						if (!(data[op].vd.data = malloc(data[op].vd.size)))
1246 							nospace();
1247 						if (bread(data[op].bp, data[op].vd.data, data[op].vd.size, data[op].vd.size, 1) != data[op].vd.size)
1248 							error(3, "%s: delta bread error", f->name);
1249 						data[op].op &= ~DELTA_BIO;
1250 						data[op].op |= DELTA_BUFFER|DELTA_FREE;
1251 						break;
1252 					case DELTA_FD:
1253 						if (!(data[op].vd.data = malloc(data[op].vd.size)))
1254 							nospace();
1255 						if (data[op].base && lseek(data[op].fd, data[op].base, SEEK_SET) != data[op].base)
1256 							error(3, "%s: delta seek error", f->name);
1257 						if (read(data[op].fd, data[op].vd.data, data[op].vd.size) != data[op].vd.size)
1258 							error(3, "%s: delta read error", f->name);
1259 						if (data[op].op & DELTA_FREE)
1260 							close(data[op].fd);
1261 						data[op].op &= ~DELTA_FD;
1262 						data[op].op |= DELTA_BUFFER|DELTA_FREE;
1263 						break;
1264 					}
1265 				}
1266 				switch (fp->variant)
1267 				{
1268 				case DELTA_88:
1269 					n = delta(data[DELTA_SRC].vd.data, data[DELTA_SRC].vd.size, data[DELTA_TAR].vd.data, data[DELTA_TAR].vd.size, data[DELTA_DEL].fd) ? -1L : lseek(data[DELTA_DEL].fd, (off_t)0, SEEK_END);
1270 					break;
1271 				case DELTA_PATCH:
1272 					error(1, "AHA %s %s/%s SRC=%d:%p TAR=%d:%p DEL=%d:%p", fp->name, ap->name, f->path, data[DELTA_SRC].vd.size, data[DELTA_SRC].vd.data, data[DELTA_TAR].vd.size, data[DELTA_TAR].vd.data, data[DELTA_DEL].vd.size, data[DELTA_DEL].vd.data);
1273 					n = 0;
1274 					break;
1275 				}
1276 			}
1277 			else
1278 			{
1279 				if (!(data[DELTA_TAR].op & DELTA_FD))
1280 					error(PANIC, "paxdelta: %s %s must be DELTA_TEMP or DELTA_FD", fp->name, dataname[DELTA_TAR]);
1281 				for (op = 0; op < elementsof(data); op++)
1282 				{
1283 					if (op == DELTA_TAR) /*NOP*/;
1284 					else if (!data[op].vd.size)
1285 					{
1286 						data[op].fd = 0;
1287 						data[op].op &= ~DELTA_FREE;
1288 					}
1289 					else switch (data[op].op & (DELTA_BIO|DELTA_BUFFER|DELTA_OUTPUT))
1290 					{
1291 					case DELTA_BIO:
1292 						if (!(data[op].vd.data = malloc(data[op].vd.size)))
1293 							nospace();
1294 						if (bread(data[op].bp, data[op].vd.data, data[op].vd.size, data[op].vd.size, 1) != data[op].vd.size)
1295 							error(3, "%s: delta bread error", f->name);
1296 						/*FALLTHROUGH*/
1297 					case DELTA_BUFFER:
1298 						if (!tmp) tmp = pathtemp(NiL, 0, NiL, error_info.id, NiL);
1299 						if ((data[op].fd = open(tmp, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, S_IRUSR)) < 0)
1300 							error(3, "%s: cannot create delta temporary file", tmp);
1301 						if (write(data[op].fd, data[op].vd.data, data[op].vd.size) != data[op].vd.size)
1302 							error(ERROR_SYSTEM|3, "%s: delta write error", f->name);
1303 						close(data[op].fd);
1304 						if ((data[op].fd = open(tmp, O_RDONLY|O_BINARY)) < 0)
1305 							error(ERROR_SYSTEM|3, "%s: cannot read delta temporary file", tmp);
1306 						if (remove(tmp))
1307 							error(ERROR_SYSTEM|1, "%s: cannot remove delta temporary file", tmp);
1308 						if (data[op].op & DELTA_BUFFER)
1309 						{
1310 							if (data[op].op & DELTA_FREE)
1311 								free(data[op].vd.data);
1312 							data[op].vd.data = 0;
1313 						}
1314 						data[op].op &= ~(DELTA_BIO|DELTA_BUFFER);
1315 						data[op].op |= DELTA_FD|DELTA_FREE;
1316 						break;
1317 					}
1318 				}
1319 				switch (fp->variant)
1320 				{
1321 				case DELTA_88:
1322 					n = update(data[DELTA_SRC].fd, data[DELTA_SRC].base, data[DELTA_DEL].fd, data[DELTA_TAR].fd) ? -1L : lseek(data[DELTA_TAR].fd, (off_t)0, SEEK_END);
1323 					break;
1324 				case DELTA_PATCH:
1325 					error(PANIC, "paxdelta: %s update not supported", fp->name);
1326 					break;
1327 				}
1328 			}
1329 		}
1330 		break;
1331 	default:
1332 		if (gen != &data[DELTA_DEL] && !data[DELTA_SRC].vd.size && !data[DELTA_DEL].vd.size)
1333 			n = 0;
1334 		else if (gen == &data[DELTA_DEL])
1335 		{
1336 			n = vddelta((Vddisc_t*)&data[DELTA_SRC], (Vddisc_t*)&data[DELTA_TAR], (Vddisc_t*)&data[DELTA_DEL]);
1337 			if (n == 15)
1338 				f->delta.same = 1;
1339 		}
1340 		else
1341 			n = vdupdate((Vddisc_t*)&data[DELTA_SRC], (Vddisc_t*)&data[DELTA_TAR], (Vddisc_t*)&data[DELTA_DEL]);
1342 		break;
1343 	}
1344 #if DEBUG
1345 	if (mp)
1346 	{
1347 		message((-5, "%s %s: %s:%s return=%ld", gen == &data[DELTA_DEL] ? "delta" : "update", fp->name, f->name, sfstruse(mp), n));
1348 		sfstrclose(mp);
1349 	}
1350 #endif
1351 	if (n < 0)
1352 	{
1353 		error(ERROR_SYSTEM|2, "%s: delta error", f->name);
1354 		return -1;
1355 	}
1356 	f->uncompressed = f->st->st_size;
1357 	f->st->st_size = n;
1358 	for (op = 0; op < elementsof(data); op++)
1359 	{
1360 		if (data[op].op & DELTA_HOLE)
1361 			holedone(data[op].fd);
1362 		switch (data[op].op & (DELTA_BUFFER|DELTA_FD|DELTA_FREE))
1363 		{
1364 		case DELTA_BUFFER|DELTA_FREE:
1365 			free(data[op].vd.data);
1366 			break;
1367 		case DELTA_FD|DELTA_FREE:
1368 			if ((data[op].op & (DELTA_TEMP|DELTA_OUTPUT)) == DELTA_OUTPUT)
1369 				closeout(ap, f, data[op].fd);
1370 			else
1371 				close(data[op].fd);
1372 			if (data[op].vd.data)
1373 				free(data[op].vd.data);
1374 			break;
1375 		}
1376 		if (data[op].op & DELTA_COUNT)
1377 		{
1378 			ap->io->expand += n;
1379 			if (data[op].op & DELTA_OUTPUT)
1380 				setfile(ap, f);
1381 		}
1382 		if (data[op].op & DELTA_LIST)
1383 			listentry(f);
1384 	}
1385 	if (gen->pfd)
1386 	{
1387 		if (bp = getbuffer(gen->fd))
1388 		{
1389 			*gen->pfd = gen->fd;
1390 			bp->next = bp->base;
1391 		}
1392 		else
1393 		{
1394 			close(gen->fd);
1395 			if ((*gen->pfd = open(state.tmp.file, O_RDONLY|O_BINARY)) < 0)
1396 				error(ERROR_SYSTEM|3, "%s: cannot read delta temporary file", state.tmp.file);
1397 			if (remove(state.tmp.file))
1398 				error(ERROR_SYSTEM|1, "%s: cannot remove delta temporary file", state.tmp.file);
1399 		}
1400 	}
1401 	return 0;
1402 }
1403