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 * 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 * 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 * 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 * 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 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 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 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 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 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 899 static char showtype(int mode) 900 { 901 return (types[ (mode & S_IFMT) >> 12 ]); 902 } 903 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