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 (c) 1995 Sun Microsystems, Inc. All Rights Reserved
24 *
25 * module:
26 * base.c
27 *
28 * purpose:
29 * routines to create, traverse, read and write the baseline database
30 *
31 * contents:
32 * manipulation:
33 * add_base, add_file_to_base, add_file_to_dir
34 * (static) add_file_to_list
35 * reading baseline:
36 * read_baseline
37 * (static) gettype
38 * writing baseline:
39 * write_baseline
40 * (static) bw_header, bw_base, bw_file, showtype
41 */
42 #ident "%W% %E% SMI"
43
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <time.h>
48
49 #include "filesync.h"
50 #include "database.h"
51 #include "messages.h"
52
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"
56
57 /*
58 * globals
59 */
60 struct base omnibase; /* dummy to hold global rules */
61 struct base *bases; /* pointer to the base list */
62
63 /*
64 * locals
65 */
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);
73
74 /*
75 * routine:
76 * add_base
77 *
78 * purpose:
79 * to find a base pair in the chain, adding it if necessary
80 *
81 * parameters:
82 * spec for source directory
83 * spec for dest directory
84 *
85 * returns:
86 * pointer to the base pair
87 *
88 */
89 struct base *
add_base(const char * src,const char * dst)90 add_base(const char *src, const char *dst)
91 { struct base *bp, **bpp;
92
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))
97 continue;
98 if (strcmp(dst, bp->b_dst_spec))
99 continue;
100
101 if (opt_debug & DBG_BASE)
102 fprintf(stderr, "BASE: FOUND base=%d, src=%s, dst=%s\n",
103 bp->b_ident, src, dst);
104 return (bp);
105 }
106
107 /* no joy, so we have to allocate one */
108 bp = malloc(sizeof (struct base));
109 if (bp == 0)
110 nomem("base structure");
111
112 /* initialize the new base */
113 memset((void *) 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);
117
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);
121 exit(ERR_FILES);
122 }
123
124 if ((bp->b_dst_name = expand(bp->b_dst_spec)) == 0) {
125 fprintf(stderr, gettext(ERR_badbase), bp->b_dst_spec);
126 exit(ERR_FILES);
127 }
128
129 /* chain it in */
130 *bpp = bp;
131
132 if (opt_debug & DBG_BASE)
133 fprintf(stderr, "BASE: ADDED base=%d, src=%s, dst=%s\n",
134 bp->b_ident, src, dst);
135
136 return (bp);
137 }
138
139 /*
140 * routine:
141 * add_file_to_list
142 *
143 * purpose:
144 * to find a file on a list, or if necessary add it to the list
145 *
146 * this is an internal routine, used only by add_file_to_base
147 * and add_file_to_dir.
148 *
149 * parameters:
150 * pointer to the list head
151 *
152 * returns:
153 * pointer to a file structure
154 *
155 * notes:
156 *
157 * list is sorted to provide some search optimization
158 *
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
164 * of the time.
165 *
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.
169 */
170 static struct file *
add_file_to_list(struct file ** pp,const char * name)171 add_file_to_list(struct file **pp, const char *name)
172 { struct file *fp, *new;
173 int rslt;
174
175 static struct file **last_list;
176 static struct file *last_file;
177
178 /*
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
182 * are:
183 * appending to the list, with appends in alphabetical order
184 * searches of the list, with searches in alphabetical order
185 */
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) {
189 fp = new->f_next;
190 /* if we're at the end, we just won */
191 if (fp == 0) {
192 pp = &new->f_next;
193 goto makeit;
194 }
195
196 /* or if the next one is what we want */
197 if (strcmp(name, fp->f_name) == 0) {
198 fp->f_flags &= ~F_NEW;
199 new = fp;
200 goto gotit;
201 }
202 }
203 }
204
205 /*
206 * our guess pointer failed, so it is exhaustive search time
207 */
208 last_list = pp;
209
210 for (fp = *pp; fp; pp = &fp->f_next, fp = *pp) {
211 rslt = strcmp(name, fp->f_name);
212
213 /* see if we got a match */
214 if (rslt == 0) {
215 fp->f_flags &= ~F_NEW;
216 new = fp;
217 goto gotit;
218 }
219
220 /* see if we should go no farther */
221 if (rslt < 0)
222 break;
223 }
224
225 makeit:
226 /*
227 * we didn't find it:
228 * pp points at where our pointer should go
229 * fp points at the node after ours
230 */
231 new = (struct file *) malloc(sizeof (*new));
232 if (new == 0)
233 nomem("file structure");
234
235 /* initialize the new node */
236 memset((void *) new, 0, sizeof (struct file));
237 new->f_name = strdup(name);
238 new->f_flags = F_NEW;
239
240 /* chain it into the list */
241 new->f_next = fp;
242 *pp = new;
243
244 gotit: /* remember this as our next guess pointer */
245 last_file = new;
246 return (new);
247 }
248
249 /*
250 * routine:
251 * add_file_to_base
252 *
253 * purpose:
254 * to add a file-node to a baseline
255 *
256 * parameters:
257 * pointer to base
258 * name of file to be added
259 *
260 * returns:
261 * pointer to file structure
262 */
263 struct file *
add_file_to_base(struct base * bp,const char * name)264 add_file_to_base(struct base *bp, const char *name)
265 { struct file *fp;
266
267 fp = add_file_to_list(&bp->b_files, name);
268 fp->f_base = bp;
269 fp->f_depth = 0;
270
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",
274 name);
275
276 return (fp);
277 }
278
279 /*
280 * routine:
281 * add_file_to_dir
282 *
283 * purpose:
284 * to add a file-node to a directory
285 *
286 * parameters:
287 * pointer to file entry for directory
288 * name of file to be added
289 *
290 * returns:
291 * pointer to file structure
292 */
293 struct file *
add_file_to_dir(struct file * dp,const char * name)294 add_file_to_dir(struct file *dp, const char *name)
295 { struct file *fp;
296
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;
300
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",
304 name);
305
306 return (fp);
307 }
308
309 /*
310 * routine:
311 * read_baseline
312 *
313 * purpose:
314 * to read in the baseline file
315 *
316 * parameters:
317 * name of baseline file
318 *
319 * returns:
320 * error mask
321 */
322 errmask_t
read_baseline(char * name)323 read_baseline(char *name)
324 { FILE *file;
325 errmask_t errs = 0;
326
327 char *s;
328 char *s1 = 0;
329 char type;
330 char *field = "???";
331
332 unsigned long l;
333 unsigned long long ll; /* intermediate for 64 bit file support */
334 int level;
335 int major, minor;
336
337 struct base *bp = 0;
338 struct file *fp;
339 struct fileinfo *ip;
340 aclent_t *ap;
341
342 struct file *dirstack[ MAX_DEPTH ];
343
344 file = fopen(name, "r");
345 if (file == NULL) {
346 fprintf(stderr, gettext(ERR_open), gettext(TXT_base),
347 name);
348 return (ERR_FILES);
349 }
350 lex_linenum = 0;
351
352 if (opt_debug & DBG_FILES)
353 fprintf(stderr, "FILE: READ BASELINE %s\n", name);
354
355 while (!feof(file)) {
356 /* find the first token on the line */
357 s = lex(file);
358
359 /* skip blank lines and comments */
360 if (s == 0 || *s == 0 || *s == '#' || *s == '*')
361 continue;
362
363 field = "keyword";
364
365 /* see if the first token is a known keyword */
366 if (strcmp(s, "VERSION") == 0 || strcmp(s, BASE_TAG) == 0) {
367 s = lex(0);
368 field = gettext(TXT_noargs);
369 if (s == 0)
370 goto bad;
371
372 major = strtol(s, &s1, 10);
373 field = gettext(TXT_badver);
374 if (*s1 != '.')
375 goto bad;
376 minor = strtol(&s1[1], 0, 10);
377
378 if (major != BASE_MAJOR || minor > BASE_MINOR) {
379 fprintf(stderr, gettext(ERR_badver),
380 major, minor, gettext(TXT_base), name);
381 errs |= ERR_FILES;
382 }
383 s1 = 0;
384 continue;
385 }
386
387 if (strcmp(s, "BASE_SRC") == 0) {
388 s = lex(0);
389 field = "source directory";
390 if (s == 0)
391 goto bad;
392 s1 = strdup(s);
393 bp = 0;
394 continue;
395 }
396
397 if (strcmp(s, "BASE_DST") == 0) {
398 s = lex(0);
399 field = "destination directory";
400 if (s == 0)
401 goto bad;
402
403 /* make sure we have a source too */
404 if (s1 == 0) {
405 field = "no source directory";
406 goto bad;
407 }
408
409 bp = add_base(s1, s);
410 free(s1);
411 s1 = 0;
412 continue;
413 }
414
415 if (strcmp(s, "FILE") == 0) {
416 /* make sure we have a base to add to */
417 if (bp == 0) {
418 field = "missing base";
419 goto bad;
420 }
421
422 s = lex(0); /* level */
423 field = "level";
424 if (s == 0 || *s == 0)
425 goto bad;
426 l = strtoul(s, 0, 0);
427 level = l;
428
429 s = lex(0); /* type */
430 field = "file type";
431 if (s == 0 || *s == 0)
432 goto bad;
433 type = *s;
434 if (gettype(type) < 0)
435 goto bad;
436
437 s = lex(0); /* name */
438 field = "file name";
439 if (s == 0 || *s == 0)
440 goto bad;
441
442 /* allocate a file structure for this entry */
443 if (level == 0)
444 fp = add_file_to_base(bp, s);
445 else
446 fp = add_file_to_dir(dirstack[level-1], s);
447
448 fp->f_flags |= F_IN_BASELINE;
449
450 /* maintain the directory stack */
451 if (level >= MAX_DEPTH) {
452 fprintf(stderr, gettext(ERR_deep), s);
453 exit(ERR_OTHER);
454 }
455
456 dirstack[ level ] = fp;
457
458 /* get a pointer to the baseline file info structure */
459 ip = &fp->f_info[ OPT_BASE ];
460
461 ip->f_type = gettype(type); /* note file type */
462
463 s = lex(0); /* modes */
464 field = "file modes";
465 if (s == 0 || *s == 0)
466 goto bad;
467 l = strtoul(s, 0, 0);
468 ip->f_mode = l;
469
470 s = lex(0); /* uid */
471 field = "file UID";
472 if (s == 0 || *s == 0)
473 goto bad;
474 l = strtoul(s, 0, 0);
475 ip->f_uid = l;
476
477 s = lex(0); /* gid */
478 field = "file GID";
479 if (s == 0 || *s == 0)
480 goto bad;
481 l = strtoul(s, 0, 0);
482 ip->f_gid = l;
483
484 s = lex(0); /* source inode */
485 field = "source i#";
486 if (s == 0 || *s == 0)
487 goto bad;
488 ll = strtoull(s, 0, 0);
489 fp->f_s_inum = (ino_t) ll;
490
491 s = lex(0); /* source major */
492 field = "source major";
493 if (s == 0 || *s == 0)
494 goto bad;
495 l = strtoul(s, 0, 0);
496 fp->f_s_maj = l;
497
498 s = lex(0); /* source minor */
499 field = "source minor";
500 if (s == 0 || *s == 0)
501 goto bad;
502 l = strtoul(s, 0, 0);
503 fp->f_s_min = l;
504
505 s = lex(0); /* source nlink */
506 field = "source nlink";
507 if (s == 0 || *s == 0)
508 goto bad;
509 l = strtoul(s, 0, 0);
510 fp->f_s_nlink = l;
511
512 s = lex(0); /* source mod */
513 field = "source modtime";
514 if (s == 0 || *s == 0)
515 goto bad;
516 l = strtoul(s, 0, 0);
517 fp->f_s_modtime = l;
518
519 s = lex(0); /* dest inode */
520 field = "destination i#";
521 if (s == 0 || *s == 0)
522 goto bad;
523 ll = strtoull(s, 0, 0);
524 fp->f_d_inum = (ino_t) ll;
525
526 s = lex(0); /* dest major */
527 field = "destination major";
528 if (s == 0 || *s == 0)
529 goto bad;
530 l = strtoul(s, 0, 0);
531 fp->f_d_maj = l;
532
533 s = lex(0); /* dest minor */
534 field = "destination minor";
535 if (s == 0 || *s == 0)
536 goto bad;
537 l = strtoul(s, 0, 0);
538 fp->f_d_min = l;
539
540 s = lex(0); /* dest nlink */
541 field = "dest nlink";
542 if (s == 0 || *s == 0)
543 goto bad;
544 l = strtoul(s, 0, 0);
545 fp->f_d_nlink = l;
546
547 s = lex(0); /* dest mod */
548 field = "dest modtime";
549 if (s == 0 || *s == 0)
550 goto bad;
551 l = strtoul(s, 0, 0);
552 fp->f_d_modtime = l;
553
554 s = lex(0); /* major or size */
555
556 if (type == 'C' || type == 'B') {
557 field = "rdev major";
558 if (s == 0 || *s == 0)
559 goto bad;
560 l = strtoul(s, 0, 0);
561 ip->f_rd_maj = l;
562
563 s = lex(0); /* minor */
564 field = "rdev minor";
565 if (s == 0 || *s == 0)
566 goto bad;
567 l = strtoul(s, 0, 0);
568 ip->f_rd_min = l;
569 } else {
570 field = "file size";
571 if (s == 0 || *s == 0)
572 goto bad;
573 ll = strtoul(s, 0, 0);
574 ip->f_size = (off_t) ll; /* size */
575 }
576
577 /*
578 * all fields after this point were added to the
579 * 1.0 format and so should be considered optional
580 */
581 s = lex(0); /* acl length ? */
582 field = "acl count";
583 if (s && *s) {
584 l = strtoul(s, 0, 0);
585 ip->f_numacls = l;
586 ip->f_acls = (aclent_t *) malloc(ip->f_numacls *
587 sizeof (aclent_t));
588 if (ip->f_acls == 0)
589 nomem("Access Control List");
590 }
591
592 continue;
593 }
594
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";
599 goto bad;
600 }
601
602 /* acl entry number */
603 s = lex(0);
604 field = "acl index";
605 if (s == 0)
606 goto bad;
607 l = strtoul(s, 0, 0);
608 if (l >= ip->f_numacls)
609 goto bad;
610 else
611 ap = &ip->f_acls[l];
612
613 /* acl entry type */
614 s = lex(0);
615 field = "acl type";
616 if (s == 0)
617 goto bad;
618 l = strtoul(s, 0, 0);
619 ap->a_type = l;
620
621 /* acl entry ID */
622 s = lex(0);
623 field = "acl id";
624 if (s == 0)
625 goto bad;
626 l = strtoul(s, 0, 0);
627 ap->a_id = l;
628
629 /* acl entry perms */
630 s = lex(0);
631 field = "acl perm";
632 if (s == 0)
633 goto bad;
634 l = strtoul(s, 0, 0);
635 ap->a_perm = l;
636
637 continue;
638 }
639
640 bad: /* log the error and continue processing to find others */
641 fprintf(stderr, gettext(ERR_badinput), lex_linenum,
642 field, name);
643 errs |= ERR_FILES;
644 }
645
646 (void) fclose(file);
647 return (errs);
648 }
649
650 /*
651 * routine:
652 * write_baseline
653 *
654 * purpose:
655 * to rewrite the baseline file
656 *
657 * parameters:
658 * name of the new baseline file
659 *
660 * returns:
661 * error mask
662 */
663 errmask_t
write_baseline(char * name)664 write_baseline(char *name)
665 { FILE *newfile;
666 errmask_t errs = 0;
667 struct base *bp;
668 char tmpname[ MAX_PATH ];
669
670 if (opt_debug & DBG_FILES)
671 fprintf(stderr, "FILE: WRITE BASELINE %s\n", name);
672
673 /* if no-touch is specified, we don't update files */
674 if (opt_notouch)
675 return (0);
676
677 /* create a temporary output file */
678 sprintf(tmpname, "%s-TMP", name);
679
680 /* create our output file */
681 newfile = fopen(tmpname, "w+");
682 if (newfile == NULL) {
683 fprintf(stderr, gettext(ERR_creat), gettext(TXT_base),
684 tmpname);
685 return (ERR_FILES);
686 }
687
688 errs |= bw_header(newfile);
689 for (bp = bases; bp; bp = bp->b_next)
690 errs |= bw_base(newfile, bp);
691
692 if (ferror(newfile)) {
693 fprintf(stderr, gettext(ERR_write), gettext(TXT_base),
694 tmpname);
695 errs |= ERR_FILES;
696 }
697
698 if (fclose(newfile)) {
699 fprintf(stderr, gettext(ERR_fclose), gettext(TXT_base),
700 tmpname);
701 errs |= ERR_FILES;
702 }
703
704 /* now switch the new file for the old one */
705 if (errs == 0)
706 if (rename(tmpname, name) != 0) {
707 fprintf(stderr, gettext(ERR_rename),
708 gettext(TXT_base), tmpname, name);
709 errs |= ERR_FILES;
710 }
711
712 return (errs);
713 }
714
715 /*
716 * routine:
717 * bw_header
718 *
719 * purpose:
720 * to write out a baseline header
721 *
722 * parameters:
723 * FILE* for the output file
724 *
725 * returns:
726 * error mask
727 *
728 * notes:
729 */
730 static errmask_t
bw_header(FILE * file)731 bw_header(FILE *file)
732 { time_t now;
733 struct tm *local;
734
735 /* figure out what time it is */
736 (void) time(&now);
737 local = localtime(&now);
738
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");
744
745 return (0);
746 }
747
748 /*
749 * routine:
750 * bw_base
751 *
752 * purpose:
753 * to write out the summary for one base-pair
754 *
755 * parameters:
756 * FILE * for the output file
757 *
758 * returns:
759 * error mask
760 *
761 * notes:
762 */
763 static errmask_t
bw_base(FILE * file,struct base * bp)764 bw_base(FILE *file, struct base *bp)
765 { struct file *fp;
766 errmask_t errs = 0;
767
768 /* see if this base is to be dropped from baseline */
769 if (bp->b_flags & F_REMOVE)
770 return (0);
771
772 fprintf(file, "\n");
773 fprintf(file, "BASE_SRC %s\n", noblanks(bp->b_src_spec));
774 fprintf(file, "BASE_DST %s\n", noblanks(bp->b_dst_spec));
775
776 for (fp = bp->b_files; fp; fp = fp->f_next)
777 errs |= bw_file(file, fp, 0);
778
779 return (errs);
780 }
781
782 /*
783 * routine:
784 * bw_file
785 *
786 * purpose:
787 * to write a file description out to the baseline
788 *
789 * parameters:
790 * output FILE
791 * pointer to file description
792 * recursion depth
793 *
794 * returns:
795 * error mask
796 *
797 * notes:
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.
801 *
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.
805 */
806 static errmask_t
bw_file(FILE * file,struct file * fp,int depth)807 bw_file(FILE *file, struct file *fp, int depth)
808 { struct file *cp;
809 int i;
810 errmask_t errs = 0;
811 long long ll; /* intermediate for 64 bit file support */
812 struct fileinfo *ip = &fp->f_info[OPT_BASE];
813
814 /* if this file is to be removed from baseline, skip it */
815 if (fp->f_flags & F_REMOVE)
816 return (0);
817
818 /*
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.
825 */
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;
837 }
838
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);
843
844 ll = fp->f_info[OPT_SRC].f_ino;
845 fprintf(file, "\t%6lld %4ld %4ld %4d 0x%08lx",
846 ll,
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);
851
852 ll = fp->f_info[OPT_DST].f_ino;
853 fprintf(file, "\t%6lld %4ld %4ld %4d 0x%08lx",
854 ll,
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);
859
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);
863 else {
864 ll = ip->f_size;
865 fprintf(file, "\t%lld", ll);
866 }
867
868 /* ACL count goes at the end because it was added */
869 fprintf(file, "\t%d", ip->f_numacls);
870
871 fprintf(file, "\n");
872
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);
877
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);
881
882 return (errs);
883 }
884
885 /*
886 * routines:
887 * gettype/showtype
888 *
889 * purpose:
890 * to convert between a file type (as found in a mode word)
891 * and a single character representation
892 *
893 * parameters/return
894 * mode word -> character
895 * character -> mode word
896 */
897 static char types[16] = "-PC?DNB?F?S?s???";
898
showtype(int mode)899 static char showtype(int mode)
900 {
901 return (types[ (mode & S_IFMT) >> 12 ]);
902 }
903
gettype(int code)904 static long gettype(int code)
905 { int i;
906
907 for (i = 0; i < 16; i++)
908 if (types[i] == code)
909 return (i << 12);
910
911 return (-1);
912 }
913