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