1 /*
2  * cpio - copy file archives in and out
3  *
4  * Gunnar Ritter, Freiburg i. Br., Germany, April 2003.
5  */
6 /*
7  * Copyright (c) 2003 Gunnar Ritter
8  *
9  * This software is provided 'as-is', without any express or implied
10  * warranty. In no event will the authors be held liable for any damages
11  * arising from the use of this software.
12  *
13  * Permission is granted to anyone to use this software for any purpose,
14  * including commercial applications, and to alter it and redistribute
15  * it freely, subject to the following restrictions:
16  *
17  * 1. The origin of this software must not be misrepresented; you must not
18  *    claim that you wrote the original software. If you use this software
19  *    in a product, an acknowledgment in the product documentation would be
20  *    appreciated but is not required.
21  *
22  * 2. Altered source versions must be plainly marked as such, and must not be
23  *    misrepresented as being the original software.
24  *
25  * 3. This notice may not be removed or altered from any source distribution.
26  */
27 
28 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
29 #define	USED	__attribute__ ((used))
30 #elif defined __GNUC__
31 #define	USED	__attribute__ ((unused))
32 #else
33 #define	USED
34 #endif
35 #if defined (SU3)
36 static const char sccsid[] USED = "@(#)pax_su3.sl	1.26 (gritter) 6/26/05";
37 #else
38 static const char sccsid[] USED = "@(#)pax.sl	1.26 (gritter) 6/26/05";
39 #endif
40 /*	Sccsid @(#)pax.c	1.26 (gritter) 6/26/05	*/
41 
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdio.h>
47 #include <stdarg.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <fnmatch.h>
51 #include <dirent.h>
52 #include <regex.h>
53 #include <wchar.h>
54 #include <time.h>
55 #include <inttypes.h>
56 
57 #include "iblok.h"
58 #include "cpio.h"
59 
60 static char		**files;
61 static int		filec;
62 static struct iblok	*filinp;
63 static char		*path;
64 static size_t		pathsz;
65 static int		pax_Hflag;
66 
67 static void		setpres(const char *);
68 static size_t		ofiles_pax(char **, size_t *);
69 static void		prtime_pax(time_t);
70 static void		parsesub(char *);
71 
72 void
flags(int ac,char ** av)73 flags(int ac, char **av)
74 {
75 	const char	optstring[] = "rwab:cdf:HikKlLno:p:s:tuvx:X";
76 	int	i;
77 	int	illegal = 0;
78 	char	*x;
79 
80 #if defined (SU3)
81 	pax = PAX_TYPE_PAX2001;
82 #else
83 	pax = PAX_TYPE_PAX1992;
84 #endif
85 	dflag = 1;
86 	uflag = 1;
87 	ofiles = ofiles_pax;
88 	prtime = prtime_pax;
89 	while ((i = getopt(ac, av, optstring)) != EOF) {
90 		switch (i) {
91 		case 'r':
92 			if (action && action != 'i')
93 				action = 'p';
94 			else
95 				action = 'i';
96 			break;
97 		case 'w':
98 			if (action && action != 'o')
99 				action = 'p';
100 			else
101 				action = 'o';
102 			break;
103 		case 'a':
104 			Aflag = 1;
105 			break;
106 		case 'b':
107 			blksiz = strtol(optarg, &x, 10);
108 			switch (*x) {
109 			case 'b':
110 				blksiz *= 512;
111 				break;
112 			case 'k':
113 				blksiz *= 1024;
114 				break;
115 			case 'm':
116 				blksiz *= 1048576;
117 				break;
118 			case 'w':
119 				blksiz *= 2;
120 				break;
121 			}
122 			if (blksiz <= 0)
123 				msg(4, -2,
124 					"Illegal size given for -b option.\n");
125 			Cflag = 1;
126 			break;
127 		case 'c':
128 			fflag = 1;
129 			break;
130 		case 'd':
131 			pax_dflag = 1;
132 			break;
133 		case 'f':
134 			Oflag = Iflag = optarg;
135 			break;
136 		case 'H':
137 			pax_Hflag = 1;
138 			break;
139 		case 'i':
140 			rflag = 1;
141 			break;
142 		case 'k':
143 			pax_kflag = 1;
144 			break;
145 		case 'K':
146 			kflag = 1;
147 			break;
148 		case 'l':
149 			lflag = 1;
150 			break;
151 		case 'L':
152 			Lflag = 1;
153 			break;
154 		case 'n':
155 			pax_nflag = 1;
156 			break;
157 		case 'o':
158 			pax_options(optarg, 1);
159 			break;
160 		case 'p':
161 			setpres(optarg);
162 			break;
163 		case 's':
164 			pax_sflag = 1;
165 			parsesub(optarg);
166 			break;
167 		case 't':
168 			aflag = 1;
169 			break;
170 		case 'u':
171 			uflag = 0;
172 			pax_uflag = 1;
173 			break;
174 		case 'v':
175 			vflag = 1;
176 			break;
177 		case 'x':
178 			if (strcmp(optarg, "cpio") == 0)
179 				fmttype = FMT_ODC;
180 			else {
181 				if (setfmt(optarg) < 0)
182 					illegal = 1;
183 			}
184 			break;
185 		case 'X':
186 			pax_Xflag = 1;
187 			break;
188 		default:
189 			illegal = 1;
190 		}
191 	}
192 	switch (action) {
193 	case 0:
194 		if (rflag || pax_kflag || pax_uflag || pax_preserve)
195 			illegal = 1;
196 		action = 'i';
197 		tflag = 1;
198 		setvbuf(stdout, NULL, _IOLBF, 0);
199 		/*FALLTHRU*/
200 	case 'i':
201 		if (aflag || pax_Xflag || lflag)
202 			illegal = 1;
203 		for (i = optind; i < ac; i++) {
204 			addg(av[i], 0);
205 			if (pax_dflag == 0) {
206 				char	*da;
207 				int	j;
208 
209 				da = smalloc(strlen(av[i]) + 2);
210 				for (j = 0; av[i][j]; j++)
211 					da[j] = av[i][j];
212 				da[j++] = '/';
213 				da[j++] = '*';
214 				da[j] = 0;
215 				addg(da, 1);
216 				free(da);
217 			}
218 		}
219 		break;
220 	case 'o':
221 		if (fflag || pax_kflag || pax_nflag || kflag)
222 			illegal = 1;
223 		if (Aflag && Oflag == NULL) {
224 			msg(3, 0, "-a requires the -f option\n");
225 			illegal = 1;
226 		}
227 		if (optind != ac) {
228 			files = &av[optind];
229 			filec = ac - optind;
230 		} else
231 			filinp = ib_alloc(0, 0);
232 		if (pax_uflag)
233 			Aflag = 1;
234 		if (Aflag == 0 && fmttype == FMT_NONE)
235 			fmttype = FMT_ODC;
236 		break;
237 	case 'p':
238 		if (fflag || blksiz || Oflag || Iflag || fmttype != FMT_NONE ||
239 				kflag)
240 			illegal = 1;
241 		if (optind == ac)
242 			illegal = 1;
243 		else if (optind+1 != ac) {
244 			files = &av[optind];
245 			filec = ac - optind - 1;
246 			optind = ac - 1;
247 		} else
248 			filinp = ib_alloc(0, 0);
249 		break;
250 	}
251 	if (illegal)
252 		usage();
253 }
254 
255 void
usage(void)256 usage(void)
257 {
258 	fprintf(stderr, "USAGE:\n\
259 \t%s [-cdnvK] [-b size] [-f file] [-s replstr] [-x hdr] [patterns]\n\
260 \t%s -r[cdiknuvK] [-b size] [-f file] [-p priv] [-s replstr] [-x hdr] [patterns]\n\
261 \t%s -w[adituvLX] [-b size] [-f file] [-s replstr] [-x hdr] [files]\n\
262 \t%s -rw[diklntuvLX] [-p priv] [-s replstr] [files] directory\n",
263 		progname, progname, progname, progname);
264 	exit(1);
265 }
266 
267 static void
setpres(const char * s)268 setpres(const char *s)
269 {
270 	s--;
271 	while (*++s) {
272 		pax_preserve &= ~PAX_P_EVERY;
273 		switch (*s) {
274 		case 'a':
275 			pax_preserve |= PAX_P_ATIME;
276 			break;
277 		case 'e':
278 			pax_preserve |= PAX_P_EVERY;
279 			break;
280 		case 'm':
281 			pax_preserve |= PAX_P_MTIME;
282 			break;
283 		case 'o':
284 			pax_preserve |= PAX_P_OWNER;
285 			break;
286 		case 'p':
287 			pax_preserve |= PAX_P_MODE;
288 			break;
289 		default:
290 			msg(2, 0, "ignoring unknown option \"-p%c\"\n",
291 					*s&0377);
292 		}
293 	}
294 	if (pax_preserve & PAX_P_EVERY)
295 		pax_preserve |= PAX_P_OWNER|PAX_P_MODE;
296 }
297 
298 int
gmatch(const char * s,const char * p)299 gmatch(const char *s, const char *p)
300 {
301 	int	val;
302 #ifdef	__GLIBC__
303 	/* avoid glibc's broken [^...] */
304 	extern char	**environ;
305 	char	**savenv = environ;
306 	char	*newenv[] = { "POSIXLY_CORRECT=", NULL };
307 	environ = newenv;
308 #endif	/* __GLIBC__ */
309 	val = fnmatch(p, s, 0) == 0;
310 #ifdef	__GLIBC__
311 	environ = savenv;
312 #endif	/* __GLIBC__ */
313 	return val;
314 }
315 
316 static const char *
nextfile(void)317 nextfile(void)
318 {
319 	char	*line = NULL;
320 	size_t	linsiz = 0, linlen;
321 
322 	if (filinp) {
323 		pax_Hflag = 0;
324 		if ((linlen=ib_getlin(filinp, &line, &linsiz, srealloc)) == 0) {
325 			filinp = NULL;
326 			return NULL;
327 		}
328 		if (line[linlen-1] == '\n')
329 			line[--linlen] = '\0';
330 		return line;
331 	} else if (filec > 0) {
332 		filec--;
333 		return *files++;
334 	} else
335 		return NULL;
336 }
337 
338 static size_t
catpath(size_t pend,const char * base)339 catpath(size_t pend, const char *base)
340 {
341 	size_t	blen = strlen(base);
342 
343 	if (pend + blen + 2 >= pathsz)
344 		path = srealloc(path, pathsz = pend + blen + 16);
345 	if (pend == 0 || path[pend-1] != '/')
346 		path[pend++] = '/';
347 	strcpy(&path[pend], base);
348 	return pend + blen;
349 }
350 
351 /*
352  * Descend the directory hierarchies given using stdin or arguments
353  * and return file names one per one.
354  */
355 static size_t
ofiles_pax(char ** name,size_t * namsiz)356 ofiles_pax(char **name, size_t *namsiz)
357 {
358 	static DIR	**dt;
359 	static int	dti, dts;
360 	static int	*pend;
361 	static dev_t	*curdev;
362 	static ino_t	*curino;
363 	struct stat	st;
364 	struct dirent	*dp;
365 	const char	*nf;
366 	int	i;
367 
368 	if (dt == NULL) {
369 		dt = scalloc(dts = 1, sizeof *dt);
370 		pend = scalloc(dts, sizeof *pend);
371 		curdev = scalloc(dts, sizeof *curdev);
372 		curino = scalloc(dts, sizeof *curino);
373 	}
374 	for (;;) {
375 		if (dti >= 0 && dt[dti] != NULL) {
376 			if ((dp = readdir(dt[dti])) != NULL) {
377 				if (dp->d_name[0] == '.' &&
378 						(dp->d_name[1] == '\0' ||
379 						 dp->d_name[1] == '.' &&
380 						 dp->d_name[2] == '\0'))
381 					continue;
382 				if (dti+1 <= dts) {
383 					dt = srealloc(dt, sizeof *dt * ++dts);
384 					pend = srealloc(pend, sizeof *pend*dts);
385 					curdev = srealloc(curdev, sizeof *curdev
386 							* dts);
387 					curino = srealloc(curino, sizeof *curino
388 							* dts);
389 				}
390 				pend[dti+1] = catpath(pend[dti], dp->d_name);
391 				if (pax_Hflag)
392 					Lflag = dti < 0;
393 				if ((Lflag ? stat : lstat)(path, &st) < 0) {
394 					emsg(2, "Error with %s of \"%s\"",
395 							lflag? "stat" : "lstat",
396 							path);
397 					errcnt++;
398 				} else if ((st.st_mode&S_IFMT) == S_IFDIR &&
399 						(pax_Xflag == 0 ||
400 						 curdev[0] == st.st_dev)) {
401 					if (Lflag) {
402 						for (i = 0; i <= dti; i++)
403 							if (st.st_dev ==
404 								curdev[i] &&
405 								st.st_ino ==
406 								curino[i]) {
407 							    if (pax ==
408 							      PAX_TYPE_PAX2001)
409 							     msg(4, 1,
410 								"Symbolic link "
411 								"loop at "
412 								"\"%s\"\n",
413 								path);
414 							    break;
415 							}
416 						if (i <= dti)
417 							break;
418 					}
419 					if ((dt[dti+1]=opendir(path)) == NULL) {
420 						emsg(2, "Cannot open directory "
421 								"\"%s\"", path);
422 						errcnt++;
423 					} else {
424 						dti++;
425 						curdev[dti] = st.st_dev;
426 						curino[dti] = st.st_ino;
427 						continue;
428 					}
429 				} else
430 					break;
431 			} else {
432 				path[pend[dti]] = '\0';
433 				closedir(dt[dti]);
434 				dt[dti--] = NULL;
435 				if (pax_Hflag)
436 					Lflag = dti < 0;
437 				break;
438 			}
439 		} else {
440 			if (pax_Hflag)
441 				Lflag = 1;
442 			while ((nf = nextfile()) != NULL &&
443 					(Lflag ? stat : lstat)(nf, &st) < 0) {
444 				emsg(2, "Error with stat of \"%s\"", nf);
445 				errcnt++;
446 			}
447 			if (nf == NULL)
448 				return 0;
449 			dti = 0;
450 			if (path)
451 				free(path);
452 			pend[dti] = strlen(nf);
453 			strcpy(path = smalloc(pathsz = pend[dti]+1), nf);
454 			if (pax_dflag || (st.st_mode&S_IFMT) != S_IFDIR) {
455 				dti = -1;
456 				break;
457 			}
458 			curdev[dti] = st.st_dev;
459 			curino[dti] = st.st_ino;
460 			if ((dt[dti] = opendir(path)) == NULL) {
461 				emsg(2, "Cannot open directory \"%s\"", path);
462 				errcnt++;
463 			}
464 		}
465 	}
466 	if (*name == NULL || *namsiz < pathsz) {
467 		free(*name);
468 		*name = smalloc(*namsiz=pathsz);
469 	}
470 	strcpy(*name, path);
471 	return pend[dti+1];
472 }
473 
474 struct	pax_had {
475 	struct pax_had	*p_next;
476 	const char	*p_name;
477 	time_t		p_mtime;
478 };
479 
480 static int	pprime = 7919;
481 
482 static int
phash(const char * s)483 phash(const char *s)
484 {
485 	uint32_t	h = 0, g;
486 
487 	s--;
488 	while (*++s) {
489 		h = (h << 4) + (*s & 0377);
490 		if (g = h & 0xf0000000) {
491 			h = h ^ (g >> 24);
492 			h = h ^ g;
493 		}
494 	}
495 	return h % pprime;
496 }
497 
498 static int
plook(const char * name,struct pax_had ** pp)499 plook(const char *name, struct pax_had **pp)
500 {
501 	static struct pax_had	**pt;
502 	uint32_t	h, had;
503 
504 	if (pt == NULL)
505 		pt = scalloc(pprime, sizeof *pt);
506 	(*pp) = pt[h = phash(name)];
507 	while (*pp != NULL) {
508 		if (strcmp((*pp)->p_name, name) == 0)
509 			break;
510 		*pp = (*pp)->p_next;
511 	}
512 	had = *pp != NULL;
513 	if (*pp == NULL) {
514 		*pp = scalloc(1, sizeof **pp);
515 		(*pp)->p_name = sstrdup(name);
516 		(*pp)->p_next = pt[h];
517 		pt[h] = *pp;
518 	}
519 	return had;
520 }
521 
522 int
pax_track(const char * name,time_t mtime)523 pax_track(const char *name, time_t mtime)
524 {
525 	struct pax_had	*pp;
526 	struct stat	st;
527 
528 	if (pax_uflag == 0 && (pax_nflag == 0 || patterns))
529 		return 1;
530 	if (action == 'i' && pax_uflag) {
531 		if (lstat(name, &st) == 0 && mtime < st.st_mtime)
532 			return 0;
533 	}
534 	if (action != 'i' || pax_nflag) {
535 		if (plook(name, &pp) != 0) {
536 			if (action != 'i' && pax_uflag == 0)
537 				return 0;
538 			if (mtime > pp->p_mtime) {
539 				pp->p_mtime = mtime;
540 				return 1;
541 			}
542 			return 0;
543 		} else
544 			pp->p_mtime = mtime;
545 	}
546 	return 1;
547 }
548 
549 static void
prtime_pax(time_t t)550 prtime_pax(time_t t)
551 {
552 	char	b[30];
553 	time_t	now;
554 
555 	time(&now);
556 	if (t > now || t < now - (6*30*86400))
557 		strftime(b, sizeof b, "%b %e  %Y", localtime(&t));
558 	else
559 		strftime(b, sizeof b, "%b %e %H:%M", localtime(&t));
560 	printf(" %s ", b);
561 }
562 
563 struct replacement {
564 	regex_t			r_re;
565 	const char		*r_rhs;
566 	int			r_nbra;
567 	enum {
568 		REPL_0	= 0,
569 		REPL_G	= 1,
570 		REPL_P	= 2
571 	}			r_flags;
572 } *rep;
573 
574 #define	NBRA	9
575 static int	ren, res;
576 static int	mb_cur_max;
577 
578 static wchar_t
nextc(char ** sc,int * np)579 nextc(char **sc, int *np)
580 {
581 	char	*p = *sc;
582 	wchar_t	wcbuf;
583 	int	len;
584 
585 	if (**sc == '\0') {
586 		*np = 0;
587 		return 0;
588 	}
589 	if (mb_cur_max == 1 || (**sc&0200) == 0) {
590 		*np = 1;
591 		return *(*sc)++ & 0377;
592 	}
593 	if ((len = mbtowc(&wcbuf, p, mb_cur_max)) < 0)
594 		msg(3, -2, "Invalid multibyte character for \"-s\" option\n");
595 	*np = len;
596 	*sc += len;
597 	return wcbuf;
598 }
599 
600 static void
parsesub(char * s)601 parsesub(char *s)
602 {
603 	int	len;
604 	char	*ps = NULL;
605 	wchar_t	seof = nextc(&s, &len);
606 	wint_t	c, d;
607 	int	nbra = 0;
608 	int	reflags;
609 
610 	if (seof == 0)
611 		goto unt;
612 	mb_cur_max = MB_CUR_MAX;
613 	ps = s;
614 	do {
615 		if ((c = nextc(&s, &len)) == seof)
616 			break;
617 		if (c == '\\') {
618 			if ((c = nextc(&s, &len)) == '(')
619 				nbra++;
620 			continue;
621 		} else if (c == '[') {
622 			d = WEOF;
623 			do {
624 				if ((c = nextc(&s, &len)) == '\0')
625 					continue;
626 				if (d == '[' && (c == ':' || c == '.' ||
627 							c == '=')) {
628 					d = c;
629 					do {
630 						if ((c=nextc(&s, &len)) == '\0')
631 							continue;
632 					} while (c != d || *s != ']');
633 					nextc(&s, &len);
634 					c = WEOF; /* reset d and continue */
635 				}
636 				d = c;
637 			} while (c != ']');
638 		}
639 	} while (*s != '\0');
640 	if (c != seof)
641 	unt:	msg(3, -2, "Unterminated argument for \"-s\" option.\n");
642 	s[-len] = '\0';
643 	if (ren <= res)
644 		rep = srealloc(rep, ++res * sizeof *rep);
645 	reflags = REG_ANGLES;
646 	if (pax >= PAX_TYPE_PAX2001)
647 		reflags |= REG_AVOIDNULL;
648 	if (regcomp(&rep[ren].r_re, ps, reflags) != 0)
649 		msg(3, -2, "Regular expression error in \"-s\" option\n");
650 	rep[ren].r_rhs = s;
651 	rep[ren].r_nbra = nbra;
652 	while ((c = nextc(&s, &len)) != 0) {
653 		if (c == '\\')
654 			c = nextc(&s, &len);
655 		else if (c == seof)
656 			break;
657 	}
658 	rep[ren].r_flags = 0;
659 	if (c == seof) {
660 		s[-len] = '\0';
661 		while ((c = nextc(&s, &len)) != '\0') {
662 			switch (c) {
663 			case 'g':
664 				rep[ren].r_flags |= REPL_G;
665 				break;
666 			case 'p':
667 				rep[ren].r_flags |= REPL_P;
668 				break;
669 			default:
670 				msg(2, 0, "Ignoring unknown -s flag \"%c\"\n",
671 						c);
672 			}
673 		}
674 	}
675 	ren++;
676 }
677 
678 #define	put(c)	((new = innew+1>=newsize ? srealloc(new, newsize+=32) : new), \
679 			new[innew++] = (c))
680 
681 int
pax_sname(char ** oldp,size_t * olds)682 pax_sname(char **oldp, size_t *olds)
683 {
684 	char	*new = NULL;
685 	size_t	newsize = 0;
686 	regmatch_t	bralist[NBRA+1];
687 	int	c, i, k, l, y, z;
688 	int	innew = 0, ef = 0;
689 	char	*inp = *oldp;
690 
691 	for (z = 0; z < ren; z++) {
692 	in:	if (regexec(&rep[z].r_re, inp, NBRA+1, bralist, ef) != 0) {
693 			if (ef == 0)
694 				continue;
695 			goto out;
696 		}
697 		for (i = 0; i < bralist[0].rm_so; i++)
698 			put(inp[i]);
699 		k = 0;
700 		while (c = rep[z].r_rhs[k++] & 0377) {
701 			y = -1;
702 			if (c == '&')
703 				y = 0;
704 			else if (c == '\\') {
705 				c = rep[z].r_rhs[k++] & 0377;
706 				if (c >= '1' && c < rep[z].r_nbra+'1')
707 					y = c - '0';
708 			}
709 			if (y >= 0)
710 				for (l = bralist[y].rm_so; l < bralist[y].rm_eo;
711 						l++)
712 					put(inp[l]);
713 			else
714 				put(c);
715 		}
716 		k = innew;
717 		for (i = bralist[0].rm_eo; inp[i]; i++)
718 			put(inp[i]);
719 		put('\0');
720 		if (rep[z].r_flags & REPL_G) {
721 			ef = REG_NOTBOL;
722 			inp = &inp[bralist[0].rm_eo];
723 			innew = k;
724 			if (bralist[0].rm_so == bralist[0].rm_eo) {
725 				if (inp[0] && (nextc(&inp, &l), inp[0]))
726 					innew++;
727 				else
728 					goto out;
729 			}
730 			goto in;
731 		}
732 	out:	if (rep[z].r_flags & REPL_P)
733 			fprintf(stderr, "%s >> %s\n", *oldp, new);
734 		free(*oldp);
735 		*oldp = new;
736 		*olds = newsize;
737 		return *new != '\0';
738 	}
739 	return 1;
740 }
741 
742 void
pax_onexit(void)743 pax_onexit(void)
744 {
745 	struct glist	*gp;
746 
747 	for (gp = patterns; gp; gp = gp->g_nxt) {
748 		if (gp->g_art)
749 			continue;
750 		if (gp->g_gotcha == 0 && (gp->g_nxt == NULL ||
751 					gp->g_nxt->g_art == 0 ||
752 					gp->g_gotcha == 0)) {
753 			msg(3, 0, "Pattern not matched: \"%s\"\n", gp->g_pat);
754 			errcnt++;
755 		}
756 	}
757 }
758