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