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