1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 1995-2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * module:
29 * eval.c
30 *
31 * purpose:
32 * routines to ascertain the current status of all of the files
33 * described by a set of rules. Some of the routines that update
34 * file status information are also called later (during reconcilation)
35 * to reflect the changes that have been made to files.
36 *
37 * contents:
38 * evaluate top level - evaluate one side of one base
39 * add_file_arg (static) add a file to the list of files to evaluate
40 * eval_file (static) stat a specific file, recurse on directories
41 * walker (static) node visitor for recursive descent
42 * note_info update a file_info structure from a stat structure
43 * do_update (static) update one file_info structure from another
44 * update_info update the baseline file_info from the prevailng side
45 * fakedata (static) make it look like one side hasn't changed
46 * check_inum (static) sanity check to detect wrong-dir errors
47 * add_glob (static) expand a wildcard in an include rule
48 * add_run (static) run a program to generate an include list
49 *
50 * notes:
51 * pay careful attention to the use of the LISTED and EVALUATE
52 * flags in each file description structure.
53 */
54
55 #pragma ident "%Z%%M% %I% %E% SMI"
56
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <libgen.h>
60 #include <unistd.h>
61 #include <string.h>
62 #include <glob.h>
63 #include <ftw.h>
64 #include <sys/mkdev.h>
65 #include <errno.h>
66
67 #include "filesync.h"
68 #include "database.h"
69 #include "messages.h"
70 #include "debug.h"
71
72 /*
73 * routines:
74 */
75 static errmask_t eval_file(struct base *, struct file *);
76 static errmask_t add_file_arg(struct base *, char *);
77 static int walker(const char *, const struct stat *, int, struct FTW *);
78 static errmask_t add_glob(struct base *, char *);
79 static errmask_t add_run(struct base *, char *);
80 static void check_inum(struct file *, int);
81 static void fakedata(struct file *, int);
82
83 /*
84 * globals
85 */
86 static bool_t usingsrc; /* this pass is on the source side */
87 static int walk_errs; /* errors found in tree walk */
88 static struct file *cur_dir; /* base directory for this pass */
89 static struct base *cur_base; /* base pointer for this pass */
90
91 /*
92 * routine:
93 * evaluate
94 *
95 * purpose:
96 * to build up a baseline description for all of the files
97 * under one side of one base pair (as specified by the rules
98 * for that base pair).
99 *
100 * parameters:
101 * pointer to the base to be evaluated
102 * source/destination indication
103 * are we restricted to new rules
104 *
105 * returns:
106 * error mask
107 *
108 * notes:
109 * we evaluate source and destination separately, and
110 * reinterpret the include rules on each side (since there
111 * may be wild cards and programs that must be evaluated
112 * in a specific directory context). Similarly the ignore
113 * rules must be interpreted anew for each base.
114 */
115 errmask_t
evaluate(struct base * bp,side_t srcdst,bool_t newrules)116 evaluate(struct base *bp, side_t srcdst, bool_t newrules)
117 { errmask_t errs = 0;
118 char *dir;
119 struct rule *rp;
120 struct file *fp;
121
122 /* see if this base is still relevant */
123 if ((bp->b_flags & F_LISTED) == 0)
124 return (0);
125
126 /* figure out what this pass is all about */
127 usingsrc = (srcdst == OPT_SRC);
128
129 /*
130 * the ignore engine maintains considerable per-base-directory
131 * state, and so must be reset at the start of a new tree.
132 */
133 ignore_reset();
134
135 /* all evaluation must happen from the appropriate directory */
136 dir = usingsrc ? bp->b_src_name : bp->b_dst_name;
137 if (chdir(dir) < 0) {
138 fprintf(stderr, gettext(ERR_chdir), dir);
139
140 /*
141 * if we have -n -o we are actually willing to
142 * pretend that nothing has changed on the missing
143 * side. This is actually useful on a disconnected
144 * notebook to ask what has been changed so far.
145 */
146 if (opt_onesided == (usingsrc ? OPT_DST : OPT_SRC)) {
147 for (fp = bp->b_files; fp; fp = fp->f_next)
148 fakedata(fp, srcdst);
149
150 if (opt_debug & DBG_EVAL)
151 fprintf(stderr, "EVAL: FAKE DATA %s dir=%s\n",
152 usingsrc ? "SRC" : "DST", dir);
153 return (0);
154 } else
155 return (ERR_NOBASE);
156 }
157
158 if (opt_debug & DBG_EVAL)
159 fprintf(stderr, "EVAL: base=%d, %s dir=%s\n",
160 bp->b_ident, usingsrc ? "SRC" : "DST", dir);
161
162 /* assemble the include list */
163 for (rp = bp->b_includes; rp; rp = rp->r_next) {
164
165 /* see if we are skipping old rules */
166 if (newrules && ((rp->r_flags & R_NEW) == 0))
167 continue;
168
169 if (rp->r_flags & R_PROGRAM)
170 errs |= add_run(bp, rp->r_file);
171 else if (rp->r_flags & R_WILD)
172 errs |= add_glob(bp, rp->r_file);
173 else
174 errs |= add_file_arg(bp, rp->r_file);
175 }
176
177 /* assemble the base-specific exclude list */
178 for (rp = bp->b_excludes; rp; rp = rp->r_next)
179 if (rp->r_flags & R_PROGRAM)
180 ignore_pgm(rp->r_file);
181 else if (rp->r_flags & R_WILD)
182 ignore_expr(rp->r_file);
183 else
184 ignore_file(rp->r_file);
185
186 /* add in the global excludes */
187 for (rp = omnibase.b_excludes; rp; rp = rp->r_next)
188 if (rp->r_flags & R_WILD)
189 ignore_expr(rp->r_file);
190 else
191 ignore_file(rp->r_file);
192
193 /*
194 * because of restriction lists and new-rules, the baseline
195 * may contain many more files than we are actually supposed
196 * to look at during the impending evaluation/analysis phases
197 *
198 * when LIST arguments are encountered within a rule, we turn
199 * on the LISTED flag for the associated files. We only evaluate
200 * files that have the LISTED flag. We turn the LISTED flag off
201 * after evaluating them because just because a file was enumerated
202 * in the source doesn't mean that will necessarily be enumerated
203 * in the destination.
204 */
205 for (fp = bp->b_files; fp; fp = fp->f_next)
206 if (fp->f_flags & F_LISTED) {
207 errs |= eval_file(bp, fp);
208 fp->f_flags &= ~F_LISTED;
209 }
210
211 /* note that this base has been evaluated */
212 bp->b_flags |= F_EVALUATE;
213
214 return (errs);
215 }
216
217 /*
218 * routine:
219 * add_file_arg
220 *
221 * purpose:
222 * to create file node(s) under a specified base for an explictly
223 * included file.
224 *
225 * parameters:
226 * pointer to associated base
227 * name of the file
228 *
229 * returns:
230 * error mask
231 *
232 * notes:
233 * the trick is that an include LIST argument need not be a file
234 * in the base directory, but may be a path passing through
235 * several intermediate directories. If this is the case we
236 * need to ensure that all of those directories are added to
237 * the tree SPARSELY since it is not intended that they be
238 * expanded during the course of evaluation.
239 *
240 * we ignore arguments that end in .. because they have the
241 * potential to walk out of the base tree, because it can
242 * result in different names for a single file, and because
243 * should never be necessary to specify files that way.
244 */
245 static errmask_t
add_file_arg(struct base * bp,char * path)246 add_file_arg(struct base *bp, char *path)
247 { int i;
248 errmask_t errs = 0;
249 struct file *dp = 0;
250 struct file *fp;
251 char *s, *p;
252 char name[ MAX_NAME ];
253
254 /*
255 * see if someone is trying to feed us a ..
256 */
257 if (strcmp(path, "..") == 0 || prefix(path, "../") ||
258 suffix(path, "/..") || contains(path, "/../")) {
259 fprintf(stderr, gettext(WARN_ignore), path);
260 return (ERR_MISSING);
261 }
262
263 /*
264 * strip off any trailing "/." or "/"
265 * since noone will miss these, it is safe to actually
266 * take them off the name. When we fall out of this
267 * loop, s will point where the null belongs. We don't
268 * actually null the end of string yet because we want
269 * to leave it pristine for error messages.
270 */
271 for (s = path; *s; s++);
272 while (s > path) {
273 if (s[-1] == '/') {
274 s--;
275 continue;
276 }
277 if (s[-1] == '.' && s > &path[1] && s[-2] == '/') {
278 s -= 2;
279 continue;
280 }
281 break;
282 }
283
284 /*
285 * skip over leading "/" and "./" (but not over a lone ".")
286 */
287 for (p = path; p < s; ) {
288 if (*p == '/') {
289 p++;
290 continue;
291 }
292 if (*p == '.' && s > &p[1] && p[1] == '/') {
293 p += 2;
294 continue;
295 }
296 break;
297 }
298
299 /*
300 * if there is nothing left, we're miffed, but done
301 */
302 if (p >= s) {
303 fprintf(stderr, gettext(WARN_ignore), path);
304 return (ERR_MISSING);
305 } else {
306 /*
307 * this is actually storing a null into the argument,
308 * but it is OK to do this because the stuff we are
309 * truncating really is garbage that noone will ever
310 * want to see.
311 */
312 *s = 0;
313 path = p;
314 }
315
316 /*
317 * see if there are any restrictions that would force
318 * us to ignore this argument
319 */
320 if (check_restr(bp, path) == 0)
321 return (0);
322
323 while (*path) {
324 /* lex off the next name component */
325 for (i = 0; path[i] && path[i] != '/'; i++)
326 name[i] = path[i];
327 name[i] = 0;
328
329 /* add it into the database */
330 fp = (dp == 0) ? add_file_to_base(bp, name)
331 : add_file_to_dir(dp, name);
332
333 /* see if this was an intermediate directory */
334 if (path[i] == '/') {
335 fp->f_flags |= F_LISTED | F_SPARSE;
336 path += i+1;
337 } else {
338 fp->f_flags |= F_LISTED;
339 path += i;
340 }
341
342 dp = fp;
343 }
344
345 return (errs);
346 }
347
348 /*
349 * routine:
350 * eval_file
351 *
352 * purpose:
353 * to evaluate one named file under a particular directory
354 *
355 * parameters:
356 * pointer to base structure
357 * pointer to file structure
358 *
359 * returns:
360 * error mask
361 * filled in evaluations in the baseline
362 *
363 * note:
364 * due to new rules and other restrictions we may not be expected
365 * to evaluate the entire tree. We should only be called on files
366 * that are LISTed, and we should only invoke ourselves recursively
367 * on such files.
368 */
369 static errmask_t
eval_file(struct base * bp,struct file * fp)370 eval_file(struct base *bp, struct file *fp)
371 { errmask_t errs = 0;
372 int rc;
373 char *name;
374 struct file *cp;
375 struct stat statb;
376
377 if (opt_debug & DBG_EVAL)
378 fprintf(stderr, "EVAL: FILE, flags=%s, name=%s\n",
379 showflags(fileflags, fp->f_flags), fp->f_name);
380
381 /* stat the file and fill in the file structure information */
382 name = get_name(fp);
383
384 #ifdef DBG_ERRORS
385 /* see if we should simulated a stat error on this file */
386 if (opt_errors && (errno = dbg_chk_error(name, usingsrc ? 's' : 'S')))
387 rc = -1;
388 else
389 #endif
390 rc = lstat(name, &statb);
391
392 if (rc < 0) {
393 if (opt_debug & DBG_EVAL)
394 fprintf(stderr, "EVAL: FAIL lstat, errno=%d\n", errno);
395 switch (errno) {
396 case EACCES:
397 fp->f_flags |= F_STAT_ERROR;
398 return (ERR_PERM);
399 case EOVERFLOW:
400 fp->f_flags |= F_STAT_ERROR;
401 return (ERR_UNRESOLVED);
402 default:
403 return (ERR_MISSING);
404 }
405 }
406
407 /* record the information we've just gained */
408 note_info(fp, &statb, usingsrc ? OPT_SRC : OPT_DST);
409
410 /*
411 * checking for ACLs is expensive, so we only do it if we
412 * have been asked to, or if we have reason to believe that
413 * the file has an ACL
414 */
415 if (opt_acls || fp->f_info[OPT_BASE].f_numacls)
416 (void) get_acls(name,
417 &fp->f_info[usingsrc ? OPT_SRC : OPT_DST]);
418
419
420 /* note that this file has been evaluated */
421 fp->f_flags |= F_EVALUATE;
422
423 /* if it is not a directory, a simple stat will suffice */
424 if ((statb.st_mode & S_IFMT) != S_IFDIR)
425 return (0);
426
427 /*
428 * as a sanity check, we look for changes in the I-node
429 * numbers associated with LISTed directories ... on the
430 * assumption that these are high-enough up on the tree
431 * that they aren't likely to change, and so a change
432 * might indicate trouble.
433 */
434 if (fp->f_flags & F_LISTED)
435 check_inum(fp, usingsrc);
436
437 /*
438 * sparse directories are on the path between a base and
439 * a listed directory. As such, we don't walk these
440 * directories. Rather, we just enumerate the LISTed
441 * files.
442 */
443 if (fp->f_flags & F_SPARSE) {
444 push_name(fp->f_name);
445
446 /* this directory isn't supposed to be fully walked */
447 for (cp = fp->f_files; cp; cp = cp->f_next)
448 if (cp->f_flags & F_LISTED) {
449 errs |= eval_file(bp, cp);
450 cp->f_flags &= ~F_LISTED;
451 }
452 pop_name();
453 } else {
454 /* fully walk the tree beneath this directory */
455 walk_errs = 0;
456 cur_base = bp;
457 cur_dir = fp;
458 nftw(get_name(fp), &walker, MAX_DEPTH, FTW_PHYS|FTW_MOUNT);
459 errs |= walk_errs;
460 }
461
462 return (errs);
463 }
464
465 /*
466 * routine:
467 * walker
468 *
469 * purpose:
470 * node visitor for recursive directory enumeration
471 *
472 * parameters:
473 * name of file
474 * pointer to stat buffer for file
475 * file type
476 * FTW structure (base name offset, walk-depth)
477 *
478 * returns:
479 * 0 continue
480 * -1 stop
481 *
482 * notes:
483 * Ignoring files is easy, but ignoring directories is harder.
484 * Ideally we would just decline to walk the trees beneath
485 * ignored directories, but ftw doesn't allow the walker to
486 * tell it to "don't enter this directory, but continue".
487 *
488 * Instead, we have to set a global to tell us to ignore
489 * everything under that tree. The variable ignore_level
490 * is set to a level, below which, everything should be
491 * ignored. Once the enumeration rises above that level
492 * again, we clear it.
493 */
494 static int
walker(const char * name,const struct stat * sp,int type,struct FTW * ftwx)495 walker(const char *name, const struct stat *sp, int type,
496 struct FTW *ftwx)
497 { const char *path;
498 struct file *fp;
499 int level;
500 int which;
501 bool_t restr;
502 static struct file *dirstack[ MAX_DEPTH + 1 ];
503 static int ignore_level = 0;
504
505 path = &name[ftwx->base];
506 level = ftwx->level;
507 which = usingsrc ? OPT_SRC : OPT_DST;
508
509 /*
510 * see if we are ignoring all files in this sub-tree
511 */
512 if (ignore_level > 0 && level >= ignore_level) {
513 if (opt_debug & DBG_EVAL)
514 fprintf(stderr, "EVAL: SKIP file=%s\n", name);
515 return (0);
516 } else
517 ignore_level = 0; /* we're through ignoring */
518
519 #ifdef DBG_ERRORS
520 /* see if we should simulated a stat error on this file */
521 if (opt_errors && dbg_chk_error(name, usingsrc ? 'n' : 'N'))
522 type = FTW_NS;
523 #endif
524
525 switch (type) {
526 case FTW_F: /* file */
527 case FTW_SL: /* symbolic link */
528 /*
529 * filter out files of inappropriate types
530 */
531 switch (sp->st_mode & S_IFMT) {
532 default: /* anything else we ignore */
533 return (0);
534
535 case S_IFCHR:
536 case S_IFBLK:
537 case S_IFREG:
538 case S_IFLNK:
539 if (opt_debug & DBG_EVAL)
540 fprintf(stderr,
541 "EVAL: WALK lvl=%d, file=%s\n",
542 level, path);
543
544 /* see if we were told to ignore this one */
545 if (ignore_check(path))
546 return (0);
547
548 fp = add_file_to_dir(dirstack[level-1], path);
549 note_info(fp, sp, which);
550
551 /* note that this file has been evaluated */
552 fp->f_flags |= F_EVALUATE;
553
554 /* see if we should check ACLs */
555 if ((sp->st_mode & S_IFMT) == S_IFLNK)
556 return (0);
557
558 if (fp->f_info[OPT_BASE].f_numacls || opt_acls)
559 (void) get_acls(name,
560 &fp->f_info[which]);
561
562 return (0);
563 }
564
565 case FTW_D: /* enter directory */
566 if (opt_debug & DBG_EVAL)
567 fprintf(stderr, "EVAL: WALK lvl=%d, dir=%s\n",
568 level, name);
569
570 /*
571 * if we have been told to ignore this directory, we should
572 * ignore all files under it. Similarly, if we are outside
573 * of our restrictions, we should ignore the entire subtree
574 */
575 restr = check_restr(cur_base, name);
576 if (restr == FALSE || ignore_check(path)) {
577 ignore_level = level + 1;
578 return (0);
579 }
580
581 fp = (level == 0) ? cur_dir :
582 add_file_to_dir(dirstack[level-1], path);
583
584 note_info(fp, sp, which);
585
586 /* see if we should be checking ACLs */
587 if (opt_acls || fp->f_info[OPT_BASE].f_numacls)
588 (void) get_acls(name, &fp->f_info[which]);
589
590 /* note that this file has been evaluated */
591 fp->f_flags |= F_EVALUATE;
592
593 /* note the parent of the children to come */
594 dirstack[ level ] = fp;
595
596 /*
597 * PROBLEM: given the information that nftw provides us with,
598 * how do we know that we have confirmed the fact
599 * that a file no longer exists. Or to rephrase
600 * this in filesync terms, how do we know when to
601 * set the EVALUATE flag for a file we didn't find.
602 *
603 * if we are going to fully scan this directory (we
604 * are completely within our restrictions) then we
605 * will be confirming the non-existance of files that
606 * used to be here. Thus any file that was in the
607 * base line under this directory should be considered
608 * to have been evaluated (whether we found it or not).
609 *
610 * if, however, we are only willing to scan selected
611 * files (due to restrictions), or the file was not
612 * in the baseline, then we should not assume that this
613 * pass will evaluate it.
614 */
615 if (restr == TRUE)
616 for (fp = fp->f_files; fp; fp = fp->f_next) {
617 if ((fp->f_flags & F_IN_BASELINE) == 0)
618 continue;
619 fp->f_flags |= F_EVALUATE;
620 }
621
622 return (0);
623
624 case FTW_DP: /* end of directory */
625 dirstack[ level ] = 0;
626 break;
627
628 case FTW_DNR: /* unreadable directory */
629 walk_errs |= ERR_PERM;
630 /* FALLTHROUGH */
631 case FTW_NS: /* unstatable file */
632 if (opt_debug & DBG_EVAL)
633 fprintf(stderr, "EVAL: walker can't stat/read %s\n",
634 name);
635 fp = (level == 0) ? cur_dir :
636 add_file_to_dir(dirstack[level-1], path);
637 fp->f_flags |= F_STAT_ERROR;
638 walk_errs |= ERR_UNRESOLVED;
639 break;
640 }
641
642 return (0);
643 }
644
645 /*
646 * routine:
647 * note_info
648 *
649 * purpose:
650 * to record information about a file in its file node
651 *
652 * parameters
653 * file node pointer
654 * stat buffer
655 * which file info structure to fill in (0-2)
656 *
657 * returns
658 * void
659 */
660 void
note_info(struct file * fp,const struct stat * sp,side_t which)661 note_info(struct file *fp, const struct stat *sp, side_t which)
662 { struct fileinfo *ip;
663 static int flags[3] = { F_IN_BASELINE, F_IN_SOURCE, F_IN_DEST };
664
665 ip = &fp->f_info[ which ];
666
667 ip->f_ino = sp->st_ino;
668 ip->f_d_maj = major(sp->st_dev);
669 ip->f_d_min = minor(sp->st_dev);
670 ip->f_type = sp->st_mode & S_IFMT;
671 ip->f_size = sp->st_size;
672 ip->f_mode = sp->st_mode & S_IAMB;
673 ip->f_uid = sp->st_uid;
674 ip->f_gid = sp->st_gid;
675 ip->f_modtime = sp->st_mtim.tv_sec;
676 ip->f_modns = sp->st_mtim.tv_nsec;
677 ip->f_nlink = sp->st_nlink;
678 ip->f_rd_maj = major(sp->st_rdev);
679 ip->f_rd_min = minor(sp->st_rdev);
680
681 /* indicate where this file has been found */
682 fp->f_flags |= flags[which];
683
684 if (opt_debug & DBG_STAT)
685 fprintf(stderr,
686 "STAT: list=%d, file=%s, mod=%08lx.%08lx, nacl=%d\n",
687 which, fp->f_name, ip->f_modtime, ip->f_modns,
688 ip->f_numacls);
689 }
690
691 /*
692 * routine:
693 * do_update
694 *
695 * purpose:
696 * to copy information from one side into the baseline in order
697 * to reflect the effects of recent reconciliation actions
698 *
699 * parameters
700 * fileinfo structure to be updated
701 * fileinfo structure to be updated from
702 *
703 * returns
704 * void
705 *
706 * note:
707 * we play fast and loose with the copying of acl chains
708 * here, but noone is going to free or reuse any of this
709 * memory anyway. None the less, I do feel embarassed.
710 */
711 static void
do_update(struct fileinfo * np,struct fileinfo * ip)712 do_update(struct fileinfo *np, struct fileinfo *ip)
713 {
714 /* get most of the fields from the designated "right" copy */
715 np->f_type = ip->f_type;
716 np->f_size = ip->f_size;
717 np->f_mode = ip->f_mode;
718 np->f_uid = ip->f_uid;
719 np->f_gid = ip->f_gid;
720 np->f_rd_maj = ip->f_rd_maj;
721 np->f_rd_min = ip->f_rd_min;
722
723 /* see if facls have to be propagated */
724 np->f_numacls = ip->f_numacls;
725 np->f_acls = ip->f_acls;
726 }
727
728 /*
729 * routine:
730 * update_info
731 *
732 * purpose:
733 * to update the baseline to reflect recent reconcliations
734 *
735 * parameters
736 * file node pointer
737 * which file info structure to trust (1/2)
738 *
739 * returns
740 * void
741 *
742 * note:
743 * after we update this I-node we run down the entire
744 * change list looking for links and update them too.
745 * This is to ensure that when subsequent links get
746 * reconciled, they are already found to be up-to-date.
747 */
748 void
update_info(struct file * fp,side_t which)749 update_info(struct file *fp, side_t which)
750 {
751 /* first update the specified fileinfo structure */
752 do_update(&fp->f_info[ OPT_BASE ], &fp->f_info[ which ]);
753
754 if (opt_debug & DBG_STAT)
755 fprintf(stderr,
756 "STAT: UPDATE from=%d, file=%s, mod=%08lx.%08lx\n",
757 which, fp->f_name, fp->f_info[ which ].f_modtime,
758 fp->f_info[ which ].f_modns);
759 }
760
761 /*
762 * routine:
763 * fakedata
764 *
765 * purpose:
766 * to populate a tree we cannot analyze with information from the baseline
767 *
768 * parameters:
769 * file to be faked
770 * which side to fake
771 *
772 * notes:
773 * We would never use this for real reconciliation, but it is useful
774 * if a disconnected notebook user wants to find out what has been
775 * changed so far. We only do this if we are notouch and oneway.
776 */
777 static void
fakedata(struct file * fp,int which)778 fakedata(struct file *fp, int which)
779 { struct file *lp;
780
781 /* pretend we actually found the file */
782 fp->f_flags |= (which == OPT_SRC) ? F_IN_SOURCE : F_IN_DEST;
783
784 /* update the specified side from the baseline */
785 do_update(&fp->f_info[ which ], &fp->f_info[ OPT_BASE ]);
786 fp->f_info[which].f_nlink = (which == OPT_SRC) ? fp->f_s_nlink :
787 fp->f_d_nlink;
788 fp->f_info[which].f_modtime = (which == OPT_SRC) ? fp->f_s_modtime :
789 fp->f_d_modtime;
790
791 for (lp = fp->f_files; lp; lp = lp->f_next)
792 fakedata(lp, which);
793 }
794
795 /*
796 * routine:
797 * check_inum
798 *
799 * purpose:
800 * sanity check inode #s on directories that are unlikely to change
801 *
802 * parameters:
803 * pointer to file node
804 * are we using the source
805 *
806 * note:
807 * the purpose of this sanity check is to catch a case where we
808 * have somehow been pointed at a directory that is not the one
809 * we expected to be reconciling against. It could happen if a
810 * variable wasn't properly set, or if we were in a new domain
811 * where an old path no longer worked. This could result in
812 * bazillions of inappropriate propagations and deletions.
813 */
814 void
check_inum(struct file * fp,int src)815 check_inum(struct file *fp, int src)
816 { struct fileinfo *ip;
817
818 /*
819 * we validate the inode number and the major device numbers ... minor
820 * device numbers for NFS devices are arbitrary
821 */
822 if (src) {
823 ip = &fp->f_info[ OPT_SRC ];
824 if (ip->f_ino == fp->f_s_inum && ip->f_d_maj == fp->f_s_maj)
825 return;
826
827 /* if file was newly created/deleted, this isn't warnable */
828 if (fp->f_s_inum == 0 || ip->f_ino == 0)
829 return;
830
831 if (opt_verbose)
832 fprintf(stdout, V_change, fp->f_name, TXT_src,
833 fp->f_s_maj, fp->f_s_min, fp->f_s_inum,
834 ip->f_d_maj, ip->f_d_min, ip->f_ino);
835 } else {
836 ip = &fp->f_info[ OPT_DST ];
837 if (ip->f_ino == fp->f_d_inum && ip->f_d_maj == fp->f_d_maj)
838 return;
839
840 /* if file was newly created/deleted, this isn't warnable */
841 if (fp->f_d_inum == 0 || ip->f_ino == 0)
842 return;
843
844 if (opt_verbose)
845 fprintf(stdout, V_change, fp->f_name, TXT_dst,
846 fp->f_d_maj, fp->f_d_min, fp->f_d_inum,
847 ip->f_d_maj, ip->f_d_min, ip->f_ino);
848 }
849
850 /* note that something has changed */
851 inum_changes++;
852 }
853
854 /*
855 * routine:
856 * add_glob
857 *
858 * purpose:
859 * to evaluate a wild-carded expression into names, and add them
860 * to the evaluation list.
861 *
862 * parameters:
863 * base
864 * expression
865 *
866 * returns:
867 * error mask
868 *
869 * notes:
870 * we don't want to allow any patterns to expand to a . because
871 * that could result in re-evaluation of a tree under a different
872 * name. The real thing we are worried about here is ".*" which
873 * is meant to pick up . files, but shouldn't pick up . and ..
874 */
875 static errmask_t
add_glob(struct base * bp,char * expr)876 add_glob(struct base *bp, char *expr)
877 { int i;
878 errmask_t errs = 0;
879 #ifndef BROKEN_GLOB
880 glob_t gt;
881 char *s;
882
883 /* expand the regular expression */
884 i = glob(expr, GLOB_NOSORT, 0, >);
885 if (i == GLOB_NOMATCH)
886 return (ERR_MISSING);
887 if (i) {
888 /* this shouldn't happen, so it's cryptic message time */
889 fprintf(stderr, "EVAL: add_glob globfail expr=%s, ret=%d\n",
890 expr, i);
891 return (ERR_OTHER);
892 }
893
894 for (i = 0; i < gt.gl_pathc; i++) {
895 /* make sure we don't let anything expand to a . */
896 s = basename(gt.gl_pathv[i]);
897 if (strcmp(s, ".") == 0) {
898 fprintf(stderr, gettext(WARN_ignore), gt.gl_pathv[i]);
899 errs |= ERR_MISSING;
900 continue;
901 }
902
903 errs |= add_file_arg(bp, gt.gl_pathv[i]);
904 }
905
906 globfree(>);
907 #else
908 /*
909 * in 2.4 the glob function was completely broken. The
910 * easiest way to get around this problem is to just ask
911 * the shell to do the work for us. This is much slower
912 * but produces virtually identical results. Given that
913 * the 2.4 version is internal use only, I probably won't
914 * worry about the performance difference (less than 2
915 * seconds for a typical filesync command, and no hit
916 * at all if they don't use regular expressions in
917 * their LIST rules).
918 */
919 char cmdbuf[MAX_LINE];
920
921 sprintf(cmdbuf, "ls -d %s 2> /dev/null", expr);
922 errs |= add_run(bp, cmdbuf);
923 #endif
924
925 return (errs);
926 }
927
928
929 /*
930 * routine:
931 * add_run
932 *
933 * purpose:
934 * to run a command and capture the names it outputs in the
935 * evaluation list.
936 *
937 * parameters
938 * base
939 * command
940 *
941 * returns:
942 * error mask
943 */
944 static errmask_t
add_run(struct base * bp,char * cmd)945 add_run(struct base *bp, char *cmd)
946 { char *s, *p;
947 FILE *fp;
948 char inbuf[ MAX_LINE ];
949 errmask_t errs = 0;
950 int added = 0;
951
952 if (opt_debug & DBG_EVAL)
953 fprintf(stderr, "EVAL: RUN %s\n", cmd);
954
955 /* run the command and collect its ouput */
956 fp = popen(cmd, "r");
957 if (fp == NULL) {
958 fprintf(stderr, gettext(ERR_badrun), cmd);
959 return (ERR_OTHER);
960 }
961
962 while (fgets(inbuf, sizeof (inbuf), fp) != 0) {
963 /* strip off any trailing newline */
964 for (s = inbuf; *s && *s != '\n'; s++);
965 *s = 0;
966
967 /* skip any leading white space */
968 for (s = inbuf; *s == ' ' || *s == '\t'; s++);
969
970 /* make sure we don't let anything expand to a . */
971 p = basename(s);
972 if (strcmp(p, ".") == 0) {
973 fprintf(stderr, gettext(WARN_ignore), s);
974 errs |= ERR_MISSING;
975 continue;
976 }
977
978 /* add this file to the list */
979 if (*s) {
980 errs |= add_file_arg(bp, s);
981 added++;
982 }
983 }
984
985 pclose(fp);
986
987 #ifdef BROKEN_GLOB
988 /*
989 * if we are being used to simulate libc glob, and we didn't
990 * return anything, we should probably assume that the regex
991 * was unable to match anything
992 */
993 if (added == 0)
994 errs |= ERR_MISSING;
995 #endif
996 return (errs);
997 }
998