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
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.
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]
23 * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved
29 * routines to create, traverse, read and write the baseline database
33 * add_base, add_file_to_base, add_file_to_dir
34 * (static) add_file_to_list
40 * (static) bw_header, bw_base, bw_file, showtype
53 #define BASE_MAJOR 1 /* base file format major rev */
54 #define BASE_MINOR 2 /* base file format minor rev */
55 #define BASE_TAG "filesync-BaseLine"
60 struct base omnibase
; /* dummy to hold global rules */
61 struct base
*bases
; /* pointer to the base list */
66 static int num_bases
; /* used to generate sequence #s */
67 static errmask_t
bw_header(FILE *); /* write out baseline header */
68 static errmask_t
bw_base(FILE *, struct base
*); /* write out one base */
69 static errmask_t
bw_file(FILE *, struct file
*, int);
70 static struct file
*add_file_to_list(struct file
**, const char *);
71 static char showtype(int);
72 static long gettype(int);
79 * to find a base pair in the chain, adding it if necessary
82 * spec for source directory
83 * spec for dest directory
86 * pointer to the base pair
90 add_base(const char *src
, const char *dst
)
91 { struct base
*bp
, **bpp
;
93 /* first see if we already have it */
94 for (bpp
= &bases
; (bp
= *bpp
) != 0; bpp
= &bp
->b_next
) {
95 /* base must match on both src and dst */
96 if (strcmp(src
, bp
->b_src_spec
))
98 if (strcmp(dst
, bp
->b_dst_spec
))
101 if (opt_debug
& DBG_BASE
)
102 fprintf(stderr
, "BASE: FOUND base=%d, src=%s, dst=%s\n",
103 bp
->b_ident
, src
, dst
);
107 /* no joy, so we have to allocate one */
108 bp
= malloc(sizeof (struct base
));
110 nomem("base structure");
112 /* initialize the new base */
113 memset(bp
, 0, sizeof (struct base
));
114 bp
->b_ident
= ++num_bases
;
115 bp
->b_src_spec
= strdup(src
);
116 bp
->b_dst_spec
= strdup(dst
);
118 /* names are expanded at run-time, and this is run-time */
119 if ((bp
->b_src_name
= expand(bp
->b_src_spec
)) == 0) {
120 fprintf(stderr
, gettext(ERR_badbase
), bp
->b_src_spec
);
124 if ((bp
->b_dst_name
= expand(bp
->b_dst_spec
)) == 0) {
125 fprintf(stderr
, gettext(ERR_badbase
), bp
->b_dst_spec
);
132 if (opt_debug
& DBG_BASE
)
133 fprintf(stderr
, "BASE: ADDED base=%d, src=%s, dst=%s\n",
134 bp
->b_ident
, src
, dst
);
144 * to find a file on a list, or if necessary add it to the list
146 * this is an internal routine, used only by add_file_to_base
147 * and add_file_to_dir.
150 * pointer to the list head
153 * pointer to a file structure
157 * list is sorted to provide some search optimization
159 * most files are in the baseline, and so come in in alphabetical
160 * order. If we keep a guess pointer to the last file we added/found,
161 * there is a better than even chance that this one should be
162 * added immediately onto the end of it ... and in so doing we
163 * can save ourselves the trouble of searching the lists most
166 * this win would be even better if the FTW traversal was sorted,
167 * but building the baseline is enough of a win to justify the
168 * feature ... but even without this we run a 60%-70% hit rate.
171 add_file_to_list(struct file
**pp
, const char *name
)
172 { struct file
*fp
, *new;
175 static struct file
**last_list
;
176 static struct file
*last_file
;
179 * start with the guess pointer, we hope to find that
180 * this request will be satisfied by the next file in
181 * the list. The two cases we are trying to optimize
183 * appending to the list, with appends in alphabetical order
184 * searches of the list, with searches in alphabetical order
186 if (last_list
== pp
&& (new = last_file
) != 0) {
187 /* we like to think we belong farther down-list */
188 if (strcmp(name
, new->f_name
) > 0) {
190 /* if we're at the end, we just won */
196 /* or if the next one is what we want */
197 if (strcmp(name
, fp
->f_name
) == 0) {
198 fp
->f_flags
&= ~F_NEW
;
206 * our guess pointer failed, so it is exhaustive search time
210 for (fp
= *pp
; fp
; pp
= &fp
->f_next
, fp
= *pp
) {
211 rslt
= strcmp(name
, fp
->f_name
);
213 /* see if we got a match */
215 fp
->f_flags
&= ~F_NEW
;
220 /* see if we should go no farther */
228 * pp points at where our pointer should go
229 * fp points at the node after ours
231 new = (struct file
*) malloc(sizeof (*new));
233 nomem("file structure");
235 /* initialize the new node */
236 memset(new, 0, sizeof (struct file
));
237 new->f_name
= strdup(name
);
238 new->f_flags
= F_NEW
;
240 /* chain it into the list */
244 gotit
: /* remember this as our next guess pointer */
254 * to add a file-node to a baseline
258 * name of file to be added
261 * pointer to file structure
264 add_file_to_base(struct base
*bp
, const char *name
)
267 fp
= add_file_to_list(&bp
->b_files
, name
);
271 if (opt_debug
& DBG_LIST
)
272 fprintf(stderr
, "LIST: base=%d, %s file=%s\n",
273 bp
->b_ident
, (fp
->f_flags
&F_NEW
) ? "NEW" : "FOUND",
284 * to add a file-node to a directory
287 * pointer to file entry for directory
288 * name of file to be added
291 * pointer to file structure
294 add_file_to_dir(struct file
*dp
, const char *name
)
297 fp
= add_file_to_list(&dp
->f_files
, name
);
298 fp
->f_base
= dp
->f_base
;
299 fp
->f_depth
= dp
->f_depth
+ 1;
301 if (opt_debug
& DBG_LIST
)
302 fprintf(stderr
, "LIST: dir=%s, %s file=%s\n",
303 dp
->f_name
, (fp
->f_flags
&F_NEW
) ? "NEW" : "FOUND",
314 * to read in the baseline file
317 * name of baseline file
323 read_baseline(char *name
)
333 unsigned long long ll
; /* intermediate for 64 bit file support */
342 struct file
*dirstack
[ MAX_DEPTH
];
344 file
= fopen(name
, "r");
346 fprintf(stderr
, gettext(ERR_open
), gettext(TXT_base
),
352 if (opt_debug
& DBG_FILES
)
353 fprintf(stderr
, "FILE: READ BASELINE %s\n", name
);
355 while (!feof(file
)) {
356 /* find the first token on the line */
359 /* skip blank lines and comments */
360 if (s
== 0 || *s
== 0 || *s
== '#' || *s
== '*')
365 /* see if the first token is a known keyword */
366 if (strcmp(s
, "VERSION") == 0 || strcmp(s
, BASE_TAG
) == 0) {
368 field
= gettext(TXT_noargs
);
372 major
= strtol(s
, &s1
, 10);
373 field
= gettext(TXT_badver
);
376 minor
= strtol(&s1
[1], 0, 10);
378 if (major
!= BASE_MAJOR
|| minor
> BASE_MINOR
) {
379 fprintf(stderr
, gettext(ERR_badver
),
380 major
, minor
, gettext(TXT_base
), name
);
387 if (strcmp(s
, "BASE_SRC") == 0) {
389 field
= "source directory";
397 if (strcmp(s
, "BASE_DST") == 0) {
399 field
= "destination directory";
403 /* make sure we have a source too */
405 field
= "no source directory";
409 bp
= add_base(s1
, s
);
415 if (strcmp(s
, "FILE") == 0) {
416 /* make sure we have a base to add to */
418 field
= "missing base";
422 s
= lex(0); /* level */
424 if (s
== 0 || *s
== 0)
426 l
= strtoul(s
, 0, 0);
429 s
= lex(0); /* type */
431 if (s
== 0 || *s
== 0)
434 if (gettype(type
) < 0)
437 s
= lex(0); /* name */
439 if (s
== 0 || *s
== 0)
442 /* allocate a file structure for this entry */
444 fp
= add_file_to_base(bp
, s
);
446 fp
= add_file_to_dir(dirstack
[level
-1], s
);
448 fp
->f_flags
|= F_IN_BASELINE
;
450 /* maintain the directory stack */
451 if (level
>= MAX_DEPTH
) {
452 fprintf(stderr
, gettext(ERR_deep
), s
);
456 dirstack
[ level
] = fp
;
458 /* get a pointer to the baseline file info structure */
459 ip
= &fp
->f_info
[ OPT_BASE
];
461 ip
->f_type
= gettype(type
); /* note file type */
463 s
= lex(0); /* modes */
464 field
= "file modes";
465 if (s
== 0 || *s
== 0)
467 l
= strtoul(s
, 0, 0);
470 s
= lex(0); /* uid */
472 if (s
== 0 || *s
== 0)
474 l
= strtoul(s
, 0, 0);
477 s
= lex(0); /* gid */
479 if (s
== 0 || *s
== 0)
481 l
= strtoul(s
, 0, 0);
484 s
= lex(0); /* source inode */
486 if (s
== 0 || *s
== 0)
488 ll
= strtoull(s
, 0, 0);
489 fp
->f_s_inum
= (ino_t
) ll
;
491 s
= lex(0); /* source major */
492 field
= "source major";
493 if (s
== 0 || *s
== 0)
495 l
= strtoul(s
, 0, 0);
498 s
= lex(0); /* source minor */
499 field
= "source minor";
500 if (s
== 0 || *s
== 0)
502 l
= strtoul(s
, 0, 0);
505 s
= lex(0); /* source nlink */
506 field
= "source nlink";
507 if (s
== 0 || *s
== 0)
509 l
= strtoul(s
, 0, 0);
512 s
= lex(0); /* source mod */
513 field
= "source modtime";
514 if (s
== 0 || *s
== 0)
516 l
= strtoul(s
, 0, 0);
519 s
= lex(0); /* dest inode */
520 field
= "destination i#";
521 if (s
== 0 || *s
== 0)
523 ll
= strtoull(s
, 0, 0);
524 fp
->f_d_inum
= (ino_t
) ll
;
526 s
= lex(0); /* dest major */
527 field
= "destination major";
528 if (s
== 0 || *s
== 0)
530 l
= strtoul(s
, 0, 0);
533 s
= lex(0); /* dest minor */
534 field
= "destination minor";
535 if (s
== 0 || *s
== 0)
537 l
= strtoul(s
, 0, 0);
540 s
= lex(0); /* dest nlink */
541 field
= "dest nlink";
542 if (s
== 0 || *s
== 0)
544 l
= strtoul(s
, 0, 0);
547 s
= lex(0); /* dest mod */
548 field
= "dest modtime";
549 if (s
== 0 || *s
== 0)
551 l
= strtoul(s
, 0, 0);
554 s
= lex(0); /* major or size */
556 if (type
== 'C' || type
== 'B') {
557 field
= "rdev major";
558 if (s
== 0 || *s
== 0)
560 l
= strtoul(s
, 0, 0);
563 s
= lex(0); /* minor */
564 field
= "rdev minor";
565 if (s
== 0 || *s
== 0)
567 l
= strtoul(s
, 0, 0);
571 if (s
== 0 || *s
== 0)
573 ll
= strtoul(s
, 0, 0);
574 ip
->f_size
= (off_t
) ll
; /* size */
578 * all fields after this point were added to the
579 * 1.0 format and so should be considered optional
581 s
= lex(0); /* acl length ? */
584 l
= strtoul(s
, 0, 0);
586 ip
->f_acls
= (aclent_t
*) malloc(ip
->f_numacls
*
589 nomem("Access Control List");
595 if (strcmp(s
, "ACL") == 0) {
596 /* make sure there is a place to put the ACL */
597 if (ip
== 0 || ip
->f_acls
== 0) {
598 field
= "ACL w/o FILE/LIST";
602 /* acl entry number */
607 l
= strtoul(s
, 0, 0);
608 if (l
>= ip
->f_numacls
)
618 l
= strtoul(s
, 0, 0);
626 l
= strtoul(s
, 0, 0);
629 /* acl entry perms */
634 l
= strtoul(s
, 0, 0);
640 bad
: /* log the error and continue processing to find others */
641 fprintf(stderr
, gettext(ERR_badinput
), lex_linenum
,
655 * to rewrite the baseline file
658 * name of the new baseline file
664 write_baseline(char *name
)
668 char tmpname
[ MAX_PATH
];
670 if (opt_debug
& DBG_FILES
)
671 fprintf(stderr
, "FILE: WRITE BASELINE %s\n", name
);
673 /* if no-touch is specified, we don't update files */
677 /* create a temporary output file */
678 sprintf(tmpname
, "%s-TMP", name
);
680 /* create our output file */
681 newfile
= fopen(tmpname
, "w+");
682 if (newfile
== NULL
) {
683 fprintf(stderr
, gettext(ERR_creat
), gettext(TXT_base
),
688 errs
|= bw_header(newfile
);
689 for (bp
= bases
; bp
; bp
= bp
->b_next
)
690 errs
|= bw_base(newfile
, bp
);
692 if (ferror(newfile
)) {
693 fprintf(stderr
, gettext(ERR_write
), gettext(TXT_base
),
698 if (fclose(newfile
)) {
699 fprintf(stderr
, gettext(ERR_fclose
), gettext(TXT_base
),
704 /* now switch the new file for the old one */
706 if (rename(tmpname
, name
) != 0) {
707 fprintf(stderr
, gettext(ERR_rename
),
708 gettext(TXT_base
), tmpname
, name
);
720 * to write out a baseline header
723 * FILE* for the output file
731 bw_header(FILE *file
)
735 /* figure out what time it is */
737 local
= localtime(&now
);
739 fprintf(file
, "%s %d.%d\n", BASE_TAG
, BASE_MAJOR
, BASE_MINOR
);
740 fprintf(file
, "#\n");
741 fprintf(file
, "# filesync baseline, last written by %s, %s",
742 cuserid((char *) 0), asctime(local
));
743 fprintf(file
, "#\n");
753 * to write out the summary for one base-pair
756 * FILE * for the output file
764 bw_base(FILE *file
, struct base
*bp
)
768 /* see if this base is to be dropped from baseline */
769 if (bp
->b_flags
& F_REMOVE
)
773 fprintf(file
, "BASE_SRC %s\n", noblanks(bp
->b_src_spec
));
774 fprintf(file
, "BASE_DST %s\n", noblanks(bp
->b_dst_spec
));
776 for (fp
= bp
->b_files
; fp
; fp
= fp
->f_next
)
777 errs
|= bw_file(file
, fp
, 0);
787 * to write a file description out to the baseline
791 * pointer to file description
798 * some of the information we write out is kept separately
799 * for source and destination files because the values should
800 * be expected to be different for different systems/copies.
802 * if a file has an unresolved conflict, we want to leave
803 * the old values in place so that we continue to compare
804 * files against the last time they agreed.
807 bw_file(FILE *file
, struct file
*fp
, int depth
)
811 long long ll
; /* intermediate for 64 bit file support */
812 struct fileinfo
*ip
= &fp
->f_info
[OPT_BASE
];
814 /* if this file is to be removed from baseline, skip it */
815 if (fp
->f_flags
& F_REMOVE
)
819 * if this node is in conflict, or if it has not been
820 * evaluated this time around, we should just leave the
821 * baseline file the way it was before. If there is a
822 * conflict, let the baseline reflect the last agreement.
823 * If the node wasn't evaluated, let the baseline reflect
824 * our last knowledge.
826 if (fp
->f_flags
& F_CONFLICT
|| (fp
->f_flags
&F_EVALUATE
) == 0) {
827 fp
->f_info
[OPT_SRC
].f_ino
= fp
->f_s_inum
;
828 fp
->f_info
[OPT_SRC
].f_nlink
= fp
->f_s_nlink
;
829 fp
->f_info
[OPT_SRC
].f_d_maj
= fp
->f_s_maj
;
830 fp
->f_info
[OPT_SRC
].f_d_min
= fp
->f_s_min
;
831 fp
->f_info
[OPT_SRC
].f_modtime
= fp
->f_s_modtime
;
832 fp
->f_info
[OPT_DST
].f_ino
= fp
->f_d_inum
;
833 fp
->f_info
[OPT_DST
].f_nlink
= fp
->f_d_nlink
;
834 fp
->f_info
[OPT_DST
].f_d_maj
= fp
->f_d_maj
;
835 fp
->f_info
[OPT_DST
].f_d_min
= fp
->f_d_min
;
836 fp
->f_info
[OPT_DST
].f_modtime
= fp
->f_d_modtime
;
839 /* write out the entry for this file */
840 fprintf(file
, "FILE %d %c %-20s 0%04o", depth
, showtype(ip
->f_type
),
841 noblanks(fp
->f_name
), ip
->f_mode
);
842 fprintf(file
, " %6ld %6ld", ip
->f_uid
, ip
->f_gid
);
844 ll
= fp
->f_info
[OPT_SRC
].f_ino
;
845 fprintf(file
, "\t%6lld %4ld %4ld %4d 0x%08lx",
847 fp
->f_info
[OPT_SRC
].f_d_maj
,
848 fp
->f_info
[OPT_SRC
].f_d_min
,
849 fp
->f_info
[OPT_SRC
].f_nlink
,
850 fp
->f_info
[OPT_SRC
].f_modtime
);
852 ll
= fp
->f_info
[OPT_DST
].f_ino
;
853 fprintf(file
, "\t%6lld %4ld %4ld %4d 0x%08lx",
855 fp
->f_info
[OPT_DST
].f_d_maj
,
856 fp
->f_info
[OPT_DST
].f_d_min
,
857 fp
->f_info
[OPT_DST
].f_nlink
,
858 fp
->f_info
[OPT_DST
].f_modtime
);
860 /* last fields are file type specific */
861 if (S_ISBLK(ip
->f_type
) || S_ISCHR(ip
->f_type
))
862 fprintf(file
, "\t%4ld %4ld", ip
->f_rd_maj
, ip
->f_rd_min
);
865 fprintf(file
, "\t%lld", ll
);
868 /* ACL count goes at the end because it was added */
869 fprintf(file
, "\t%d", ip
->f_numacls
);
873 /* if this file has ACLs, we have to write them out too */
874 for (i
= 0; i
< ip
->f_numacls
; i
++)
875 fprintf(file
, "ACL %d %d %ld %o\n", i
, ip
->f_acls
[i
].a_type
,
876 ip
->f_acls
[i
].a_id
, ip
->f_acls
[i
].a_perm
);
878 /* then enumerate all of the children (if any) */
879 for (cp
= fp
->f_files
; cp
; cp
= cp
->f_next
)
880 errs
|= bw_file(file
, cp
, depth
+ 1);
890 * to convert between a file type (as found in a mode word)
891 * and a single character representation
894 * mode word -> character
895 * character -> mode word
897 static char types
[16] = "-PC?DNB?F?S?s???";
899 static char showtype(int mode
)
901 return (types
[ (mode
& S_IFMT
) >> 12 ]);
904 static long gettype(int code
)
907 for (i
= 0; i
< 16; i
++)
908 if (types
[i
] == code
)