1 /*$Id: spf_util.c,v 1.34 2000/05/27 13:47:43 jens Exp $*/
2 /*
3  * Copyright (c) 1997, 1998, 1999
4  *      Jens A. Nilsson, jnilsson@ludd.luth.se. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #ifdef MEMDEBUG
29 #include <memdebug.h>
30 #endif
31 
32 #if defined(SunOS)
33 #define NO_FNMATCH
34 #endif
35 
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/stat.h>
39 #include <sys/time.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <dirent.h>
45 #include <limits.h>
46 #include <unistd.h>
47 
48 #ifdef NO_STRLCPY
49 #include "strlcpy.h"
50 #endif
51 
52 #include "e_err.h"
53 #include "spegla.h"
54 #include "spf_util.h"
55 #include "parserow.h"
56 
57 #ifdef NOPROTOS
58 int snprintf(char *str, size_t size, const char *format, ...);
59 char *strdup(char *);
60 int utimes(const char *path, const struct timeval *times);
61 int lstat(const char *path, struct stat *sb);
62 int symlink(const char *name1, const char *name2);
63 int readlink(const char *path, char *buf, size_t bufsiz);
64 #endif
65 
66 char cur_dir[PATH_MAX] = ".";
67 static char cur_file[PATH_MAX] = "";
68 
69 extern int sp_do_delete;
70 extern int sp_max_delete;
71 extern int sp_deletes;
72 extern int sp_dirs_traversed;
73 extern int sp_errors;
74 extern unsigned int sp_retrytime;
75 
76 
77 /* the data structure containing all files to skip */
78 struct cl_sps_que *skip;
79 
80 /* the data structure containing all links to treat as directories */
81 struct cl_spt_que *treatasdir;
82 
83 /*
84  * have_dotdot_ref()
85  *
86  * Used by cur_dir_cd() to determine if f might result in a change
87  * to directory above current directory.
88  * Return 1 if the result might result in a change to a direcotry
89  * above current directory.
90  */
91 static int
have_dotdot_ref(char * f)92 have_dotdot_ref(char *f)
93 {
94 	if (strstr(f, "/../") != NULL)
95 		return 1;
96 	return 0;
97 }
98 
99 /*
100  * cur_dir_cd()
101  *
102  * Changes to a directory under cur_dir. Changes with a path containing
103  * ".." is not allowed.
104  * Returns the new current directory.
105  */
106 char *
cur_dir_cd(char * str)107 cur_dir_cd(char *str)
108 {
109 	size_t	len;
110 
111 	/* we don't want to go over the dir we start from */
112 	if (have_dotdot_ref(str))
113 		return NULL;
114 
115 	if ((strlen(str) + strlen(cur_dir) + 1) > (PATH_MAX - 2))
116 		return NULL;				/* no overflow */
117 	len = strlen(cur_dir);
118 	if (len > 0 && cur_dir[len - 1] != '/')
119 		(void) strcat(cur_dir, "/");
120 	(void) strcat(cur_dir, str);
121 	(void) strcat(cur_dir, "/");
122 	return cur_dir;
123 }
124 
125 /*
126  * cur_dir_cdup()
127  *
128  * Changes to the directory above current directory in cur_dir.
129  * Doesn't go above start directory. Tries to keep a '/' as last
130  * characher in  cur_dir.
131  */
132 char *
cur_dir_cdup(void)133 cur_dir_cdup(void)
134 {
135 	size_t	len;
136 	int		i;
137 
138 	len = strlen(cur_dir);
139 
140 	/* if cur_dir is empty or only containing a single '.' just return */
141 	if (len == 0 || (len == 1 && cur_dir[0] == '.'))
142 		return cur_dir;
143 
144 	/* if cur_dir ends with a '/' strip it off */
145 	if (cur_dir[len - 1] == '/') {
146 		cur_dir[len - 1] = '\0';
147 		len--;
148 	}
149 
150 	/* find last '/' in cur_dir */
151 	for (i = (int)len; (i > 0) && (cur_dir[i] != '/'); i--);
152 
153 	/* terminate the string just behind the '/' */
154 	cur_dir[i + 1] = '\0';
155 
156 	return cur_dir;
157 }
158 
159 /*
160  * full_name()
161  *
162  * Returns the path name of f relative to start dir.
163  * A buffer is needed to hold the returned path in.
164  * Used by spf_full_name() and spf_full_name2().
165  */
166 static char *
full_name(char * f,char * buf,size_t len)167 full_name(char *f, char *buf, size_t len)
168 {
169 	/* we don't want to go over the dir we start from */
170 	if (have_dotdot_ref(f))
171 		return NULL;
172 
173 	/* no overflow */
174 	if ((strlen(f) + strlen(cur_dir)) > (len - 2))
175 		return NULL;
176 
177 	(void) sprintf(buf, "%s%s", cur_dir, f);
178 	return buf;
179 }
180 
181 /*
182  * spf_full_name()
183  *
184  * Returns the path name of spf relative to start dir.
185  * cur_file is used as buffer to return.
186  */
187 char *
spf_full_name(struct sp_file * spf)188 spf_full_name(struct sp_file *spf)
189 {
190 	return spf_full_name2(spf, cur_file, (size_t)PATH_MAX);
191 }
192 
193 /*
194  * spf_full_name2()
195  *
196  * Returns the path name of spf relative to start dir.
197  * A buffer is needed to hold the returned path in.
198  */
199 char *
spf_full_name2(struct sp_file * spf,char * buf,size_t len)200 spf_full_name2(struct sp_file *spf, char *buf, size_t len)
201 {
202 	return full_name(spf->spf_name, buf, len);
203 }
204 
205 /*
206  * spf_use_tmp_name()
207  *
208  * Generate a unique file name and replace spf_name with it.
209  *
210  * A call to spf_restore_name() later will rename spf_name file
211  * the original file name and free the allocated buffer for the
212  * temporary filename.
213  */
214 char *
spf_use_tmp_name(struct sp_file * spf)215 spf_use_tmp_name(struct sp_file *spf)
216 {
217 	char	*str;
218 	char	buf[PATH_MAX], buf2[PATH_MAX];
219 	int		fd;
220 
221 	if ((str = malloc(PATH_MAX)) == NULL)
222 		return NULL;
223 	snprintf(buf, PATH_MAX, ".in.%s.XXX", spf->spf_name);
224 	if (full_name(buf, buf2, PATH_MAX) == NULL)
225 		return NULL;
226 	if ((fd = mkstemp(buf2)) < 0)
227 		return NULL;
228 	close(fd);		/* can't pass the descriptor in any convenient way */
229 	if ((str = strdup(rindex(buf2, '/') + 1)) == NULL)
230 		return NULL;
231 	spf->spf_orig_name = spf->spf_name;
232 	spf->spf_name = str;
233 	return str;
234 }
235 
236 /*
237  * spf_restore()
238  *
239  * Restore the name of the downloaded file. Free the allocated buffer for the
240  * temporary filename.
241  */
242 int
spf_restore_name(struct sp_file * spf)243 spf_restore_name(struct sp_file *spf)
244 {
245 	struct	stat sb;
246 	char	*f, *o, buf[PATH_MAX];
247 	int		ret_val;
248 
249 	if (spf->spf_orig_name == NULL)
250 		return 0;
251 
252 	ret_val = 0;
253 	f = spf_full_name(spf);
254 	if (lstat(f, &sb) < 0 && errno == ENOENT) {
255 		/* the temp file does not exist anylonger,
256 		 * give back the original name
257 		 */
258 		goto ret;
259 	}
260 
261 	o = full_name(spf->spf_orig_name, buf, sizeof(buf));
262 	if (rename(f, o) < 0) {
263 		e_warn("failed to rename %s to %s", o, f);
264 		ret_val = -1;
265 	}
266 
267 ret:
268 	free(spf->spf_name);
269 	spf->spf_name = spf->spf_orig_name;
270 	spf->spf_orig_name = NULL;
271 	return ret_val;
272 }
273 
274 
275 /*
276  * symlink_cd()
277  *
278  * Used by spf_symlink_resolve() to resolve what a symlink points to
279  */
280 static int
symlink_cd(char * dir,int dir_len,char * buf,size_t buf_siz)281 symlink_cd(char *dir, int dir_len, char *buf, size_t buf_siz)
282 {
283 	size_t	len;
284 
285 	len = strlen(buf);
286 	if ((buf_siz - len - 1)<= dir_len)
287 		return -1;
288 
289 	switch (dir_len) {
290 	case 0:
291 		return 0;
292 	case 1:
293 		if (*dir == '.')
294 			return 0;
295 		break;
296 	case 2:
297 		if (strncmp(dir, "..", (size_t)2) == 0)
298 			goto cd_up;
299 		break;
300 	default:
301 		break;
302 	}
303 	(void)memcpy(buf + len, dir, (size_t) dir_len);
304 	len += dir_len;
305 	if (buf[len - 1] != '/') {
306 		buf[len++] = '/';
307 	}
308 	buf[len] = '\0';
309 
310 	return 0;
311 
312 cd_up:
313 	/* change ./../ to ../ to keep consistent */
314 	if (strcmp(buf, "./../") == 0) {
315 		/* don't forget the NULL terminator */
316 		(void)memmove(buf, buf + 2, len - 1);
317 		len -= 2;
318 	}
319 
320 	/* first entry goes up */
321 	if (strncmp(buf, "../", (size_t)3) == 0) {
322 		(void) strcat(buf, "../");
323 		return 0;
324 	}
325 
326 	/* path is empty */
327 	if (len == 0) {
328 		(void) strcpy(buf, "../");
329 		return 0;
330 	}
331 
332 	/* up one dir */
333 	for (len--; buf[len] != '/' && len != 0; len--);
334 	if (buf[len] == '/') {
335 		/* up at root doesn't change anything */
336 		if (len == 0)
337 			return 0;
338 		buf[len + 1] = '\0';
339 		return 0;
340 	}
341 
342 	/* empty path */
343 	strcpy(buf, "./");
344 	return 0;
345 }
346 
347 /*
348  * spf_symlink_resolve()
349  *
350  * Resolves what path a symlink points to. Returned path is the
351  * shortest path regarding .. and so.
352  */
353 
354 char *
spf_symlink_resolve(struct sp_file * spf,char * buf,size_t len)355 spf_symlink_resolve(struct sp_file *spf, char *buf, size_t len)
356 {
357 	char	*p;
358 	size_t	plen;
359 	int		i, j;
360 
361 	p = spf->spf_symlink;
362 	if (p == NULL)
363 		return NULL;
364 
365 	buf[0] = '\0';
366 
367 	/* The symlink is relative */
368 	if (*p != '/') {
369 		/* buffer overrun check */
370 		if (len <= strlen(cur_dir))
371 			return NULL;
372 
373 		strcat(buf, "./");
374 		if (strncmp(cur_dir, "./", (size_t)2) == 0)
375 			(void) strcat(buf, cur_dir + 2);
376 		else
377 			(void) strcat(buf, cur_dir);
378 	}
379 
380 	plen = strlen(p);
381 	i = j = 0;
382 	while (i <= plen) {
383 		if (p[i] == '/' || p[i] == '\0') {
384 			if (symlink_cd(p + j, i - j, buf, len) < 0)
385 				return NULL;
386 			j = i + 1;
387 			i++;
388 		}
389 		i++;
390 	}
391 	/* remove trailing '/' */
392 	plen = strlen(buf);
393 	if (plen > 0 && buf[plen - 1] == '/')
394 		buf[plen - 1] = '\0';
395 	return buf;
396 }
397 
398 /*
399  * spf_is_a_skip_find()
400  *
401  * Used by spf_is_a_skip() to determine whether a file should be skipped.
402  * Returns 1 the file is to be skipped.
403  */
404 static int
spf_is_a_skip_find(struct sp_skip * sps,struct sp_file * spf)405 spf_is_a_skip_find(struct sp_skip *sps, struct sp_file *spf)
406 {
407 	char	buf[PATH_MAX];
408 	char	*p;
409 	int		match;
410 
411 	p = spf_full_name2(spf, buf, sizeof(buf));
412 	p++; /* remove the . in ./ */
413 	match = sps_match(sps, p);
414 	if (match || spf->spf_type != SYMLINK)
415 		return match;
416 
417 	/* sps_type == SYMLINK */
418 	p = spf_symlink_resolve(spf, buf, sizeof(buf));
419 	match = sps_match(sps, p);
420 	return match;
421 }
422 
423 /*
424  * spf_is_a_skip()
425  *
426  * Returns 1 if spf is a file to skip.
427  */
428 int
spf_is_a_skip(struct sp_file * spf)429 spf_is_a_skip(struct sp_file *spf)
430 {
431 	char	buf[PATH_MAX];
432 	char	*p;
433 	cl_sps_find_f	f_fun;
434 
435 	if (skip == NULL || skip == CL_NEXT(skip))
436 		return 0;
437 
438 	/* LINTED don't obfuscate the cl_sps_find() more that necessary */
439 	f_fun = (cl_sps_find_f) spf_is_a_skip_find;
440 
441 	if (cl_sps_find(skip, NULL, f_fun, (struct sp_skip *)spf) < 0)
442 		return 0;
443 
444 	p = spf_full_name2(spf, buf, sizeof(buf));
445 	e_warnx("skipping %s file '%s'",
446 		spf->spf_opt & SPFO_LOCAL ? "local" : "remote", p);
447 	return 1;
448 }
449 
450 /*
451  * spf_is_a_treatasdir_find()
452  *
453  * Used by spf_is_a_skip() to determine whether a link should be treated
454  * as a directory.
455  * Returns 1 the file is to be treated as a directory.
456  */
457 static int
spf_is_a_treatasdir_find(struct sp_treatasdir * spt,struct sp_file * spf)458 spf_is_a_treatasdir_find(struct sp_treatasdir *spt, struct sp_file *spf)
459 {
460 	char	buf[PATH_MAX];
461 	char	*p;
462 	int		match;
463 
464 	if (spf->spf_type != SYMLINK)
465 		return 0;
466 
467 	if (!spt->spt_deref) {
468 		p = spf_full_name2(spf, buf, sizeof(buf));
469 		p++; /* remove the . in ./ */
470 	} else {
471 #if 1
472 		p = spf_symlink_resolve(spf, buf, sizeof(buf));
473 		p++; /* remove the . in ./ */
474 #else
475 		if (spf->spf_symlink[0] != '/' &&
476 			strncmp(spf->spf_symlink, "..", (size_t)2) != 0) {
477 			(void) snprintf(buf, (size_t)PATH_MAX, "./%s", spf->spf_symlink);
478 			p = buf;
479 		} else
480 			p = spf->spf_symlink;
481 #endif
482 	}
483 
484 	match = spt_match(spt, p);
485 #if 0
486 	e_warnx("comparing '%s' with regexp: %s", p, match ? "match" : "not match");
487 #endif
488 	return match;
489 }
490 
491 
492 /*
493  * spf_is_a_treatasdir()
494  *
495  * Returns 1 if spf is a file to treat as directory.
496  */
497 int
spf_is_a_treatasdir(struct sp_file * spf)498 spf_is_a_treatasdir(struct sp_file *spf)
499 {
500 	char	buf[PATH_MAX], buf2[PATH_MAX];
501 	char	*p, *p2;
502 	cl_spt_find_f	f_fun;
503 
504 	if (treatasdir == NULL || treatasdir == CL_NEXT(treatasdir))
505 		return 0;
506 
507 	/* LINTED don't obfuscate the cl_sps_find() more that necessary */
508 	f_fun = (cl_spt_find_f) spf_is_a_treatasdir_find;
509 
510 	if (cl_spt_find(treatasdir, NULL, f_fun, (struct sp_treatasdir *)spf) < 0)
511 		return 0;
512 
513 	p = spf_full_name2(spf, buf, sizeof(buf));
514 	p2 = spf_symlink_resolve(spf, buf2, sizeof(buf2));
515 	e_warnx("treating %s link '%s -> %s' pointing at '%s' as directory '%s'",
516 		spf->spf_opt & SPFO_LOCAL ? "local" : "remote",
517 		p, spf->spf_symlink, p2, p);
518 	return 1;
519 }
520 
521 /*
522  * spf_time_cmp()
523  *
524  * Compares the time of two sp_file's.
525  * Returns the result in the same style as strcmp()
526  */
527 int
spf_time_cmp(struct sp_file * f1,struct sp_file * f2)528 spf_time_cmp(struct sp_file *f1, struct sp_file *f2)
529 {
530 	if ((f1->spf_time - f1->spf_tgranul) <= (f2->spf_time + f2->spf_tgranul) &&
531 		(f1->spf_time + f1->spf_tgranul) >= (f2->spf_time - f2->spf_tgranul))
532 		return 0;
533 	if (f1->spf_time > f2->spf_time)
534 		return 1;
535 	return -1;
536 }
537 
538 /*
539  * spf_name_cmp()
540  *
541  * Compares the name of two sp_file's.
542  * Returns the result in the same style as strcmp()
543  */
544 int
spf_name_cmp(struct sp_file * f1,struct sp_file * f2)545 spf_name_cmp(struct sp_file *f1, struct sp_file *f2)
546 {
547 	if (f1 == NULL && f2 == NULL)
548 		return 0;
549 	if (f1 == NULL) {
550 		if (*f2->spf_name != '\0')
551 			return -*f2->spf_name;
552 		return -1;
553 	}
554 	if (f2 == NULL) {
555 		if (*f2->spf_name != '\0')
556 			return *f1->spf_name;
557 		return 1;
558 	}
559 	return strcmp(f1->spf_name, f2->spf_name);
560 }
561 
562 /*
563  * spf_push_comp()
564  *
565  * Used as argument to heap_spf_init() to maintain heap.
566  */
567 int
spf_push_comp(struct sp_file * f1,struct sp_file * f2)568 spf_push_comp(struct sp_file * f1, struct sp_file * f2)
569 {
570 	return -spf_name_cmp(f1, f2);
571 }
572 
573 /*
574  * spf_unalloc()
575  *
576  * Frees a sp_file.
577  */
578 void
spf_unalloc(struct sp_file * f)579 spf_unalloc(struct sp_file *f)
580 {
581 	if (f != NULL) {
582 		if (f->spf_name != NULL)
583 			free(f->spf_name);
584 		if (f->spf_orig_name != NULL)
585 			free(f->spf_orig_name);
586 		if (f->spf_symlink != NULL)
587 			free(f->spf_symlink);
588 		free(f);
589 	}
590 }
591 
592 /*
593  * spf_new_time()
594  *
595  * Updates the time of the file in the filesystem according to
596  * spf->spf_time.
597  */
598 int
spf_new_time(struct sp_file * spf)599 spf_new_time(struct sp_file *spf)
600 {
601 	char	*f;
602 	struct	timeval ftime[2];
603 
604 	f = spf_full_name(spf);
605 	ftime[0].tv_sec = spf->spf_time;
606 	ftime[1].tv_sec = spf->spf_time;
607 	ftime[0].tv_usec = ftime[1].tv_usec = 0;
608 	if (utimes(f, &ftime[0]) < 0) {
609 		e_warn("failed to set time on %s", f);
610 		return -1;
611 	}
612 	return 0;
613 }
614 
615 /* #ifdef DEBUG */
616 
617 #if 0
618 void
619 spf_show_que(struct cl_spf_que *q)
620 {
621 	struct	cl_spf_que *p;
622 	struct	sp_file *spf;
623 
624 	for (p = CL_NEXT(q); p != q; p = CL_NEXT(p)) {
625 		spf = CL_ELM(p);
626 		(void) printf("name = %s, time = %lu, mode = %o, "
627 			"type = %d, size = %lu\n",
628 			spf->spf_name, spf->spf_time, spf->spf_mode,
629 			spf->spf_type, (unsigned long) spf->spf_size);
630 	}
631 }
632 #endif
633 
634 /*
635  * is_dir()
636  *
637  * Used by rm_f() and rm_rf()
638  * Returns 0 if f isn't a directory.
639  */
640 static int
is_dir(char * f)641 is_dir(char *f)
642 {
643 	struct stat sb;
644 
645 	if (lstat(f, &sb) < 0)
646 		return 0;
647 	return S_ISDIR(sb.st_mode);
648 }
649 
650 /*
651  * rm_f()
652  *
653  * Used by rm_rf() and spf_rm()
654  * Removes _one_ file or diretory. Will exit if the maximum number
655  * of deletes is exceeded.
656  */
657 static int
rm_f(char * f)658 rm_f(char *f)
659 {
660 	int	ret;
661 
662 	if (sp_max_delete != 0 && sp_max_delete <= sp_deletes)
663 		e_errx(1, "Maximum number of deletes exceeded, quit");
664 	ret = is_dir(f) ? rmdir(f) : unlink(f);
665 	if (ret == 0)
666 		sp_deletes++;
667 	else
668 		e_warn("rm_f: failed to remove %s", f);
669 	return ret;
670 }
671 
672 /*
673  * rm_rf()
674  *
675  * Used by spf_rm()
676  * Removes a hierarchy of diretories. Will exit if the maximum number
677  * of deletes is exceeded.
678  */
679 static int
rm_rf(char * f)680 rm_rf(char *f)
681 {
682 	DIR		*dirp;
683 	struct	dirent *dp;
684 	char	file_buf[PATH_MAX];
685 
686 	/* XXX this shouldn't be needed */
687 	if (have_dotdot_ref(f)) {
688 		e_warnx("rm_rf: have .. reference in path: %s", f);
689 		return -1;
690 	}
691 
692 	if (!is_dir(f))
693 		return rm_f(f);
694 
695 	if (sp_max_delete != 0 && sp_max_delete <= sp_deletes)
696 		e_errx(1, "Maximum number of deletes exceeded, quit");
697 
698 	if ((dirp = opendir(f)) == NULL)
699 		return -1;
700 
701 	while ((dp = readdir(dirp)) != NULL) {
702 		if (strcmp("..", dp->d_name) == 0 || strcmp(".", dp->d_name) == 0)
703 			continue;
704 		(void) snprintf(file_buf, (size_t)PATH_MAX, "%s/%s", f, dp->d_name);
705 		file_buf[PATH_MAX - 1] = '\0';
706 		if (is_dir(file_buf)) {
707 			(void) rm_rf(file_buf);
708 			continue;
709 		}
710 		if (sp_max_delete != 0 && sp_max_delete <= sp_deletes) {
711 			(void) closedir(dirp);
712 			e_errx(1, "Maximum number of deletes exceeded, quit");
713 		}
714 		(void) rm_f(file_buf);
715 	}
716 	(void) closedir(dirp);
717 	return rm_f(f);
718 }
719 
720 /*
721  * spf_rm()
722  *
723  * Removes a hierarchy of diretories if sp_do_delete is set. Will exit
724  * if the maximum number of deletes is exceeded.
725  */
726 int
spf_rm(struct sp_file * spf)727 spf_rm(struct sp_file *spf)
728 {
729 	char	*f;
730 
731 	/* only delete if we are supposed to */
732 	if (sp_do_delete == 0)
733 		return 0;
734 
735 	f = spf_full_name(spf);
736 	if (spf->spf_type == DIRECTORY) {
737 		e_warnx("rm -rf %s", f);
738 		if (rm_rf(f) < 0) {
739 			e_warn("failed to remove %s", f);
740 			return -1;
741 		}
742 		return 0;
743 	}
744 	e_warnx("rm -f %s", f);
745 	if (rm_f(f) < 0) {
746 		e_warn("failed to remove %s", f);
747 		return -1;
748 	}
749 	return 0;
750 }
751 
752 /*
753  * spf_clone()
754  *
755  * Returns a copy of a sp_file
756  */
757 struct sp_file *
spf_clone(struct sp_file * spf)758 spf_clone(struct sp_file *spf)
759 {
760 	struct sp_file *p;
761 
762 	p = malloc(sizeof(*p));
763 	(void) memcpy(p, spf, sizeof(*p));
764 
765 	if (spf->spf_name != NULL)
766 		p->spf_name = strdup(spf->spf_name);
767 
768 	if (spf->spf_symlink != NULL)
769 		p->spf_symlink = strdup(spf->spf_symlink);
770 
771 	return p;
772 }
773 
774 /*
775  * spf_chmod()
776  *
777  * Updates the modes of the file in the filesystem according to
778  * spf->spf_mode with mode bitwise OR:ed in.
779  */
780 int
spf_chmod(struct sp_file * spf,mode_t mode)781 spf_chmod(struct sp_file *spf, mode_t mode)
782 {
783 	char	*f;
784 
785 	f = spf_full_name(spf);
786 	if (spf->spf_opt & SPFO_LOG_CHMOD)
787 		e_warnx("chmod %o %s", (unsigned) spf->spf_mode|mode, f);
788 	if (chmod(f, spf->spf_mode|mode) < 0) {
789 		e_warn("failed to chmod %s", f);
790 		return -1;
791 	}
792 	return 0;
793 }
794 
795 /*
796  * spf_mkdir()
797  *
798  * Creates a diretory if SPFO_DONT_CREATE isn't set in spf_opt.
799  */
800 int
spf_mkdir(struct sp_file * spf)801 spf_mkdir(struct sp_file *spf)
802 {
803 	char	*f;
804 
805 	/* Directory already exists, report success */
806 	if (spf->spf_opt & SPFO_DONT_CREATE)
807 		return 0;
808 
809 	f = spf_full_name(spf);
810 	e_warnx("mkdir %s", f);
811 	if (mkdir(f, spf->spf_mode | S_IRWXU) < 0) {
812 		e_warn("mkdir %s failed", f);
813 		return -1;
814 	}
815 	return 0;
816 }
817 
818 /*
819  * spf_symlink()
820  *
821  * Creates a symlink according to spf_name and spf_symlink.
822  */
823 int
spf_symlink(struct sp_file * spf)824 spf_symlink(struct sp_file *spf)
825 {
826 	e_warnx("ln -s %s %s", spf->spf_symlink, spf_full_name(spf));
827 	if (symlink(spf->spf_symlink, spf_full_name(spf)) < 0) {
828 		e_warn("ln -s %s %s failed", spf->spf_symlink, spf_full_name(spf));
829 		return -1;
830 	}
831 	return 0;
832 }
833 
834 /*
835  * spf_stat_init()
836  *
837  * Creates a sp_file with the values from a file in the filesystem.
838  */
839 int
spf_stat_init(char * f,struct sp_file ** spf)840 spf_stat_init(char *f, struct sp_file **spf)
841 {
842 	char	*cf, *buf;
843 	int		ret;
844 	struct	sp_file *s;
845 	struct	stat sb;
846 
847 	s = calloc((size_t)1, sizeof(*s));
848 	if (s == NULL)
849 		return -1;
850 	s->spf_name = strdup(f);
851 	cf = full_name(f, cur_file, (size_t)PATH_MAX);
852 
853 	if (lstat(cf, &sb) < 0) {
854 		e_warn("failed to lstat %s", cf);
855 		goto ret_bad;
856 	}
857 
858 	s->spf_time = sb.st_mtime;
859 	s->spf_tgranul = 0;
860 
861 	s->spf_size = sb.st_size;
862 	s->spf_mode = sb.st_mode & 0xfff; /* mask out permissions */
863 	switch(sb.st_mode & S_IFMT) {
864 	case S_IFDIR:
865 		s->spf_type = DIRECTORY;
866 		break;
867 	case S_IFREG:
868 		s->spf_type = PLAINFILE;
869 		break;
870 	case S_IFLNK:
871 		s->spf_type = SYMLINK;
872 		break;
873 	default:
874 		s->spf_type = UNKNOWN;
875 	}
876 	if (s->spf_type == SYMLINK) {
877 		buf = alloca((size_t)(PATH_MAX + 1));
878 		if ((ret = readlink(cf, buf, (size_t)PATH_MAX)) < 0) {
879 			e_warn("failed to readlink %s", cf);
880 			goto ret_bad;
881 		}
882 		buf[ret] = '\0';
883 		s->spf_symlink = strdup(buf);
884 	}
885 	s->spf_opt |= SPFO_LOCAL;	/* mark spf as local */
886 	*spf = s;
887 	return 0;
888 
889 ret_bad:
890 	spf_unalloc(s);
891 	return -1;
892 }
893 
894 
895 /* init the sp_skip struct */
896 struct sp_skip *
sps_init(const char * arg)897 sps_init(const char *arg)
898 {
899 	size_t	len;
900 	int		cflags;
901 	struct	sp_skip *sps;
902 
903 	len = strlen(arg);
904 	if ((sps = calloc((size_t)1, sizeof(*sps) + len + 1)) == NULL)
905 		return NULL;
906 	/* LINTED save us from one calloc */
907 	sps->sps_name = (char *)(sps + 1);
908 	(void) strcpy(sps->sps_name, arg);
909 	cflags = 0;
910 	cflags |= REG_EXTENDED;		/* extended RE's */
911 	cflags |= REG_NOSUB;		/* only report match or no match */
912 	sps->sps_reg_errno = regcomp(&sps->sps_reg, sps->sps_name, cflags);
913 	if (sps->sps_reg_errno != 0) {
914 		free(sps);
915 		return NULL;
916 	}
917 	return sps;
918 }
919 
920 /* init the sp_treatasdir struct */
921 struct sp_treatasdir *
spt_init(const char * arg)922 spt_init(const char *arg)
923 {
924 	size_t	len;
925 	int		cflags;
926 	struct	sp_treatasdir *spt;
927 
928 	len = strlen(arg);
929 	if ((spt = calloc((size_t)1, sizeof(*spt) + len + 1)) == NULL)
930 		return NULL;
931 	/* LINTED save us from one calloc */
932 	spt->spt_name = (char *)(spt + 1);
933 	(void) strcpy(spt->spt_name, arg);
934 	cflags = 0;
935 	cflags |= REG_EXTENDED;		/* extended RE's */
936 	cflags |= REG_NOSUB;		/* only report match or no match */
937 	spt->spt_reg_errno = regcomp(&spt->spt_reg, spt->spt_name, cflags);
938 	spt->spt_reg_need_regfree = (spt->spt_reg_errno == 0);
939 	return spt;
940 }
941 
942 /* return 1 if error */
943 int
spt_error(struct sp_treatasdir * spt)944 spt_error(struct sp_treatasdir *spt)
945 {
946 	return (spt->spt_reg_errno != 0);
947 }
948 
949 /* make a human readable error message */
950 char *
spt_strerror(struct sp_treatasdir * spt)951 spt_strerror(struct sp_treatasdir *spt)
952 {
953 	size_t	buf_size;
954 
955 	/* calculate needed size of buf */
956 	buf_size = regerror(spt->spt_reg_errno, &spt->spt_reg, NULL, (size_t)0);
957 
958 	/* only malloc if spt_buf too small */
959 	if (spt->spt_buf_size < buf_size) {
960 		if (spt->spt_buf != NULL)
961 			free(spt->spt_buf);
962 		spt->spt_buf = malloc(buf_size);
963 		if (spt->spt_buf == NULL) {
964 			spt->spt_buf_size = 0;
965 			return NULL;
966 		}
967 		spt->spt_buf_size = buf_size;
968 	}
969 
970 	(void) regerror(spt->spt_reg_errno, &spt->spt_reg,
971 		spt->spt_buf, spt->spt_buf_size);
972 	return spt->spt_buf;
973 }
974 
975 /* free the sp_skip struct */
976 void
spt_unalloc(struct sp_treatasdir * spt)977 spt_unalloc(struct sp_treatasdir *spt)
978 {
979 	if (spt->spt_buf != NULL)
980 		free(spt->spt_buf);
981 
982 	/* only regfree after a successful regcomp */
983 	if (spt->spt_reg_need_regfree)
984 		regfree(&spt->spt_reg);
985 
986 	free(spt);
987 }
988 
989 
990 
991 /* return 1 if error */
992 int
sps_error(struct sp_skip * sps)993 sps_error(struct sp_skip *sps)
994 {
995 	return (sps->sps_reg_errno != 0);
996 }
997 
998 /* make a human readable error message */
999 char *
sps_strerror(struct sp_skip * sps)1000 sps_strerror(struct sp_skip *sps)
1001 {
1002 	size_t	buf_size;
1003 
1004 	/* calculate needed size of buf */
1005 	buf_size = regerror(sps->sps_reg_errno, &sps->sps_reg, NULL, (size_t)0);
1006 
1007 	/* only malloc if sps_buf too small */
1008 	if (sps->sps_buf_size < buf_size) {
1009 		if (sps->sps_buf != NULL)
1010 			free(sps->sps_buf);
1011 		sps->sps_buf = malloc(buf_size);
1012 		if (sps->sps_buf == NULL) {
1013 			sps->sps_buf_size = 0;
1014 			return NULL;
1015 		}
1016 		sps->sps_buf_size = buf_size;
1017 	}
1018 
1019 	(void) regerror(sps->sps_reg_errno, &sps->sps_reg,
1020 		sps->sps_buf, sps->sps_buf_size);
1021 	return sps->sps_buf;
1022 }
1023 
1024 /* free the sp_skip struct */
1025 void
sps_unalloc(struct sp_skip * sps)1026 sps_unalloc(struct sp_skip *sps)
1027 {
1028 	if (sps->sps_buf != NULL)
1029 		free(sps->sps_buf);
1030 
1031 	regfree(&sps->sps_reg);
1032 
1033 	free(sps);
1034 }
1035 
1036 int
sps_match(struct sp_skip * sps,char * name)1037 sps_match(struct sp_skip *sps, char *name)
1038 {
1039 	regmatch_t *pmatch;
1040 	size_t	nmatch;
1041 	int		eflags;
1042 
1043 	eflags = 0;
1044 	nmatch = 0;		/* only want to know wether it matched or not */
1045 	pmatch = NULL;	/* no need for storage */
1046 	sps->sps_reg_errno = regexec(&sps->sps_reg, name, nmatch, pmatch, eflags);
1047 	return (sps->sps_reg_errno == 0);
1048 }
1049 
1050 int
spt_match(struct sp_treatasdir * spt,char * name)1051 spt_match(struct sp_treatasdir *spt, char *name)
1052 {
1053 	regmatch_t *pmatch;
1054 	size_t	nmatch;
1055 	int		eflags;
1056 
1057 	eflags = 0;
1058 	nmatch = 0;		/* only want to know wether it matched or not */
1059 	pmatch = NULL;	/* no need for storage */
1060 	spt->spt_reg_errno = regexec(&spt->spt_reg, name, nmatch, pmatch, eflags);
1061 	return (spt->spt_reg_errno == 0);
1062 }
1063 
1064 /***** functions for recursive list of files *****/
1065 
1066 static char *
parse_dir_name(char * str)1067 parse_dir_name(char *str)
1068 {
1069 	size_t	len;
1070 
1071 	str += strspn(str, "./");
1072 	/* remove trailing '\r', '\n' and ':' */
1073 	len = strlen(str);
1074 	if (str[len - 1] == '\n')
1075 		str[len - 1] = '\0';
1076 	len = strlen(str);
1077 	if (str[len - 1] == '\r')
1078 		str[len - 1] = '\0';
1079 	len = strlen(str);
1080 	if (str[len - 1] != ':')
1081 		return NULL;
1082 	str[len - 1] = '\0';
1083 	return strdup(str);
1084 }
1085 
1086 static int
is_empty_line(char * str)1087 is_empty_line(char *str)
1088 {
1089 	size_t	l;
1090 
1091 	l = strspn(str, " \n\r");
1092 	return (str[l] == '\0');
1093 }
1094 
1095 static void
spd_unalloc(struct sp_dir * spd)1096 spd_unalloc(struct sp_dir *spd)
1097 {
1098 	if (spd->spd_spdl != NULL)
1099 		sl_spd_free(spd->spd_spdl, spd_unalloc);
1100 	if (spd->spd_full_name != NULL)
1101 		free(spd->spd_full_name);
1102 }
1103 
1104 static struct sp_dir *
spd_init(FILE * f)1105 spd_init(FILE *f)
1106 {
1107 	char	buf[PATH_MAX];
1108 	struct	sp_dir *spd;
1109 	long	pos;
1110 	int		oerrno;
1111 
1112 	if ((spd = malloc(sizeof(*spd))) == NULL)
1113 		return NULL;
1114 
1115 	do {
1116 		if ((pos = ftell(f)) < 0)
1117 			goto ret_bad;
1118 		if (fgets(buf, (int)sizeof(buf), f) == NULL)
1119 			goto ret_bad;
1120 	} while (is_empty_line(buf));
1121 
1122 	if ((spd->spd_full_name = parse_dir_name(buf)) == NULL) {
1123 		if (errno == ENOMEM)
1124 			goto ret_bad;
1125 	} else {
1126 		if ((spd->spd_name = rindex(spd->spd_full_name, '/')) == NULL)
1127 			spd->spd_name = spd->spd_full_name;
1128 		else
1129 			spd->spd_name++;
1130 		if ((pos = ftell(f)) < 0)
1131 			goto ret_bad;
1132 		if (fgets(buf, (int)sizeof(buf), f) == NULL)
1133 			goto ret_bad;
1134 	}
1135 
1136 	if (strncmp(buf + 1, "otal", (size_t)4) != 0) {
1137 		if (fseek(f, pos, SEEK_SET) < 0)
1138 			goto ret_bad;
1139 	}
1140 
1141 	spd->spd_begin = pos;
1142 	do {
1143 		if (fgets(buf, (int)sizeof(buf), f) == NULL) {
1144 			if (feof(f))
1145 				break;
1146 			goto ret_bad;
1147 		}
1148 	} while (is_empty_line(buf) == 0);
1149 	/* We are at end of file or at the empty line that follows each
1150 	 * directory.
1151 	 */
1152 	if ((spd->spd_end = ftell(f)) < 0)
1153 		goto ret_bad;
1154 	return spd;
1155 
1156 ret_bad:
1157 	oerrno = errno;
1158 	free(spd);
1159 	errno = oerrno;
1160 	return NULL;
1161 }
1162 
1163 static u_int32_t
spd_key(const char * str)1164 spd_key(const char *str)
1165 {
1166 	u_int32_t	key;
1167 	size_t		len;
1168 	char		*s;
1169 	int			i;
1170 
1171 	key = 0;
1172 	len = strlen(str);
1173 	s = (char *)&key;
1174 	for (i = 0; i < 4 && len > 0; i++, len--)
1175 		s[i] = str[len];
1176 	return key;
1177 }
1178 
1179 static int
spd_find(struct sp_dir * spd,const char * str,struct sp_dir ** p)1180 spd_find(struct sp_dir *spd, const char *str, struct sp_dir **p)
1181 {
1182 	u_int32_t	key;
1183 	struct		skip_spd_list_node *pos;
1184 
1185 	key = spd_key(str);
1186 	if (sl_spd_find_pos(spd->spd_spdl, key, &pos, p) < 0)
1187 		goto ret_not_found;
1188 	while (strcmp((*p)->spd_name, str) != 0) {
1189 		if (sl_spd_walk(spd->spd_spdl, &pos, p) < 0)
1190 			goto ret_not_found;
1191 		if (spd_key((*p)->spd_name) != key)
1192 			goto ret_not_found;
1193 	}
1194 	return 0;
1195 
1196 ret_not_found:
1197 	errno = ENOENT;
1198 	return -1;
1199 }
1200 
1201 static char *
mystrsep(char ** strp,const char * delim)1202 mystrsep(char **strp, const char *delim)
1203 {
1204 	char	*str;
1205 
1206 	while ((str = strsep(strp, delim)) != NULL) {
1207 		if (*str != '\0')
1208 			return str;
1209 	}
1210 	return str;
1211 }
1212 
1213 static int
spd_find_path(struct sp_dir_idx * spdi,const char * path,struct sp_dir ** spd)1214 spd_find_path(struct sp_dir_idx *spdi, const char *path, struct sp_dir **spd)
1215 {
1216 	char	buf[PATH_MAX];
1217 	char	*str, *s;
1218 	struct	sp_dir *p1, *p2;
1219 
1220 	strlcpy(buf, path, PATH_MAX);
1221 	str = buf;
1222 	if ((s = mystrsep(&str, "/")) == NULL)
1223 		s = str;
1224 	p1 = spdi->spdi_spd;
1225 	for ( ;; ) {
1226 		if (spd_find(p1, s, &p2) < 0)
1227 			return -1;
1228 		p1 = p2;
1229 		if ((s = mystrsep(&str, "/")) == NULL)
1230 			return 0;
1231 	}
1232 }
1233 
1234 static int
spdi_insert_spd(struct sp_dir_idx * spdi,struct sp_dir * spd)1235 spdi_insert_spd(struct sp_dir_idx *spdi, struct sp_dir *spd)
1236 {
1237 	char	buf[PATH_MAX], *str;
1238 	struct	sp_dir *p;
1239 	size_t	len;
1240 
1241 	strlcpy(buf, spd->spd_full_name, PATH_MAX);
1242 	for ( ;; ) {
1243 		str = rindex(buf, '/');
1244 		if ((len = strlen(str)) == 0)
1245 			return -1;
1246 		if (str[len - 1] != '/')
1247 			break;
1248 		str[len - 1] = '\0';
1249 	}
1250 	if (spd_find_path(spdi, buf, &p) < 0)
1251 		return -1;
1252 	if (sl_spd_ins(p->spd_spdl, spd_key(spd->spd_name), spd) < 0)
1253 		return -1;
1254 	return 0;
1255 }
1256 
1257 static int
spdi_index_file(struct sp_dir_idx * spdi)1258 spdi_index_file(struct sp_dir_idx *spdi)
1259 {
1260 	struct	sp_dir *spd;
1261 
1262 	if ((spd = spd_init(spdi->spdi_file)) == NULL)
1263 		return -1;
1264 
1265 	spdi->spdi_spd = spd;
1266 
1267 	for ( ;; ) {
1268 		if ((spd = spd_init(spdi->spdi_file)) == NULL) {
1269 			if (feof(spdi->spdi_file))
1270 				return 0;
1271 			return -1;
1272 		}
1273 		if (spdi_insert_spd(spdi, spd) < 0)
1274 			return -1;
1275 	}
1276 }
1277 
1278 struct sp_dir_idx *
spdi_init(const char * filename,const char * path)1279 spdi_init(const char *filename, const char *path)
1280 {
1281 	struct	sp_dir_idx *spdi;
1282 	int		oerrno;
1283 
1284 	if ((spdi = calloc(1, sizeof(*spdi))) == NULL)
1285 		return NULL;
1286 	if ((spdi->spdi_file = fopen(filename, "r")) == NULL)
1287 		goto ret_bad;
1288 	if ((spdi->spdi_path = strdup(path)) == NULL)
1289 		goto ret_bad;
1290 	if (spdi_index_file(spdi) < 0)
1291 		goto ret_bad;
1292 	return spdi;
1293 
1294 ret_bad:
1295 	oerrno = errno;
1296 	if (spdi->spdi_file != NULL)
1297 		fclose(spdi->spdi_file);
1298 	if (spdi->spdi_path != NULL)
1299 		free(spdi->spdi_path);
1300 	if (spdi->spdi_spd != NULL)
1301 		spd_unalloc(spdi->spdi_spd);
1302 	free(spdi);
1303 	errno = oerrno;
1304 	return NULL;
1305 }
1306 
1307 void
spdi_unalloc(struct sp_dir_idx * spdi)1308 spdi_unalloc(struct sp_dir_idx *spdi)
1309 {
1310 	if (spdi == NULL)
1311 		return;
1312 	spd_unalloc(spdi->spdi_spd);
1313 	if (spdi->spdi_file != NULL)
1314 		fclose(spdi->spdi_file);
1315 	if (spdi->spdi_path != NULL)
1316 		free(spdi->spdi_path);
1317 	free(spdi);
1318 }
1319 
1320 int
spdi_chdir(struct sp_dir_idx * spdi,const char * dir)1321 spdi_chdir(struct sp_dir_idx *spdi, const char *dir)
1322 {
1323 	if (spd_find_path(spdi, dir, &spdi->spdi_cur_dir) < 0)
1324 		return -1;
1325 	if (fseek(spdi->spdi_file, spdi->spdi_cur_dir->spd_begin, SEEK_SET) < 0)
1326 		return -1;
1327 	return 0;
1328 }
1329 
1330 int
spdi_next_file(struct sp_dir_idx * spdi,struct sp_file ** spf)1331 spdi_next_file(struct sp_dir_idx *spdi, struct sp_file **spf)
1332 {
1333 	char	buf[PATH_MAX];
1334 	struct	sp_file *p;
1335 	long	pos;
1336 
1337 	if ((pos = ftell(spdi->spdi_file)) < 0)
1338 		return -1;
1339 	if (pos > spdi->spdi_cur_dir->spd_end) {
1340 		*spf = NULL;	/* XXX to report end dir listing */
1341 		return -1;
1342 	}
1343 	if (fgets(buf, PATH_MAX, spdi->spdi_file) == NULL)
1344 		return -1;
1345 	if ((p = parse_row(buf)) == NULL)
1346 		return -1;
1347 	*spf = p;
1348 	return 0;
1349 }
1350 
1351