home *** CD-ROM | disk | FTP | other *** search
/ Chip 2000 May / Chip_2000-05_cd2.bin / dosutils / gtar109 / diffarch.c < prev    next >
C/C++ Source or Header  |  1991-01-14  |  16KB  |  687 lines

  1. /* Diff files from a tar archive.
  2.    Copyright (C) 1988 Free Software Foundation
  3.  
  4. This file is part of GNU Tar.
  5.  
  6. GNU Tar is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU Tar is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Tar; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /*
  21.  * Diff files from a tar archive.
  22.  *
  23.  * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
  24.  *
  25.  * @(#) diffarch.c 1.10 87/11/11 - gnu
  26.  */
  27.  
  28. #include <stdio.h>
  29. #include <errno.h>
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32.  
  33. #ifdef BSD42
  34. #include <sys/file.h>
  35. #endif
  36.  
  37. #ifndef MSDOS
  38. #include <sys/ioctl.h>
  39. #if !defined(USG) || defined(HAVE_MTIO)
  40. #include <sys/mtio.h>
  41. #endif
  42. #endif
  43.  
  44. #ifdef USG
  45. #include <fcntl.h>
  46. #endif
  47.  
  48. #ifdef MSDOS
  49. #include <fcntl.h>
  50. #endif
  51.  
  52. /* Some systems don't have these #define's -- we fake it here. */
  53. #ifndef O_RDONLY
  54. #define    O_RDONLY    0
  55. #endif
  56. #ifndef    O_NDELAY
  57. #define    O_NDELAY    0
  58. #endif
  59.  
  60. #ifndef S_IFLNK
  61. #define lstat stat
  62. #endif
  63.  
  64. #ifndef MSDOS
  65. extern int errno;            /* From libc.a */
  66. #endif
  67. extern char *valloc();            /* From libc.a */
  68.  
  69. #include "tar.h"
  70. #include "port.h"
  71. #include "rmt.h"
  72.  
  73. extern union record *head;        /* Points to current tape header */
  74. extern struct stat hstat;        /* Stat struct corresponding */
  75. extern int head_standard;        /* Tape header is in ANSI format */
  76.  
  77. extern void print_header();
  78. extern void skip_file();
  79. extern void skip_extended_headers();
  80.  
  81. extern FILE *msg_file;
  82.  
  83. int now_verifying = 0;        /* Are we verifying at the moment? */
  84.  
  85. int    diff_fd;        /* Descriptor of file we're diffing */
  86.  
  87. char    *diff_buf = 0;        /* Pointer to area for reading
  88.                        file contents into */
  89.  
  90. char    *diff_dir;        /* Directory contents for LF_DUMPDIR */
  91.  
  92. int different = 0;
  93.  
  94. /*struct sp_array *sparsearray;
  95. int         sp_ar_size = 10;*/
  96. /*
  97.  * Initialize for a diff operation
  98.  */
  99. diff_init()
  100. {
  101.  
  102.     /*NOSTRICT*/
  103.     diff_buf = (char *) valloc((unsigned)blocksize);
  104.     if (!diff_buf) {
  105.         msg("could not allocate memory for diff buffer of %d bytes\n",
  106.             blocksize);
  107.         exit(EX_ARGSBAD);
  108.     }
  109. }
  110.  
  111. /*
  112.  * Diff a file against the archive.
  113.  */
  114. void
  115. diff_archive()
  116. {
  117.     register char *data;
  118.     int check, namelen;
  119.     int err;
  120.     long offset;
  121.     struct stat filestat;
  122.     char linkbuf[NAMSIZ+3];
  123.     int compare_chunk();
  124.     int compare_dir();
  125.     dev_t    dev;
  126.     ino_t    ino;
  127.     char *get_dir_contents();
  128.     long from_oct();
  129.     long lseek();
  130.  
  131.     errno = EPIPE;            /* FIXME, remove perrors */
  132.  
  133.     saverec(&head);            /* Make sure it sticks around */
  134.     userec(head);            /* And go past it in the archive */
  135.     decode_header(head, &hstat, &head_standard, 1);    /* Snarf fields */
  136.  
  137.     /* Print the record from 'head' and 'hstat' */
  138.     if (f_verbose) {
  139.         if(now_verifying)
  140.             fprintf(msg_file,"Verify ");
  141.         print_header();
  142.     }
  143.  
  144.     switch (head->header.linkflag) {
  145.  
  146.     default:
  147.         msg("Unknown file type '%c' for %s, diffed as normal file\n",
  148.             head->header.linkflag, head->header.name);
  149.         /* FALL THRU */
  150.  
  151.     case LF_OLDNORMAL:
  152.     case LF_NORMAL:
  153.     case LF_SPARSE:
  154.     case LF_CONTIG:
  155.         /*
  156.          * Appears to be a file.
  157.          * See if it's really a directory.
  158.          */
  159.         namelen = strlen(head->header.name)-1;
  160.         if (head->header.name[namelen] == '/')
  161.             goto really_dir;
  162.  
  163.  
  164.         if(do_stat(&filestat)) {
  165.             if (head->header.isextended)
  166.                 skip_extended_headers();
  167.             skip_file((long)hstat.st_size);
  168.             different++;
  169.             goto quit;
  170.         }
  171.  
  172.         if ((filestat.st_mode & S_IFMT) != S_IFREG) {
  173.             fprintf(msg_file, "%s: not a regular file\n",
  174.                 head->header.name);
  175.             skip_file((long)hstat.st_size);
  176.             different++;
  177.             goto quit;
  178.         }
  179.  
  180.         filestat.st_mode &= ~S_IFMT;
  181.         if (filestat.st_mode != hstat.st_mode)
  182.             sigh("mode");
  183.         if (filestat.st_uid  != hstat.st_uid)
  184.             sigh("uid");
  185.         if (filestat.st_gid  != hstat.st_gid)
  186.             sigh("gid");
  187.         if (filestat.st_mtime != hstat.st_mtime)
  188.             sigh("mod time");
  189.         if (head->header.linkflag != LF_SPARSE &&
  190.                 filestat.st_size != hstat.st_size) {
  191.             sigh("size");
  192.             skip_file((long)hstat.st_size);
  193.             goto quit;
  194.         }
  195.  
  196.         diff_fd = open(head->header.name, O_NDELAY|O_RDONLY|O_BINARY);
  197.  
  198.         if (diff_fd < 0 && !f_absolute_paths) {
  199.             char tmpbuf[NAMSIZ+2];
  200.  
  201.             tmpbuf[0]='/';
  202.             strcpy(&tmpbuf[1],head->header.name);
  203.             diff_fd=open(tmpbuf, O_NDELAY|O_RDONLY|O_BINARY);
  204.         }
  205.         if (diff_fd < 0) {
  206.             msg_perror("cannot open %s",head->header.name);
  207.             if (head->header.isextended)
  208.                 skip_extended_headers();
  209.             skip_file((long)hstat.st_size);
  210.             different++;
  211.             goto quit;
  212.         }
  213.         /*
  214.          * Need to treat sparse files completely differently here.
  215.          */
  216.         if (head->header.linkflag == LF_SPARSE)
  217.             diff_sparse_files(hstat.st_size);
  218.         else
  219.             wantbytes((long)(hstat.st_size),compare_chunk);
  220.  
  221.         check = close(diff_fd);
  222.         if (check < 0)
  223.             msg_perror("Error while closing %s",head->header.name);
  224.  
  225.     quit:
  226.         break;
  227.  
  228.     case LF_LINK:
  229.         if(do_stat(&filestat))
  230.             break;
  231.         dev = filestat.st_dev;
  232.         ino = filestat.st_ino;
  233.         err = stat(head->header.linkname, &filestat);
  234.         if (err < 0) {
  235.             if (errno==ENOENT) {
  236.                 fprintf(msg_file, "%s: does not exist\n",head->header.name);
  237.             } else {
  238.                 msg_perror("cannot stat file %s",head->header.name);
  239.             }
  240.             different++;
  241.             break;
  242.         }
  243.         if(filestat.st_dev!=dev || filestat.st_ino!=ino) {
  244.             fprintf(msg_file, "%s not linked to %s\n",head->header.name,head->header.linkname);
  245.             break;
  246.         }
  247.         break;
  248.  
  249. #ifdef S_IFLNK
  250.     case LF_SYMLINK:
  251.         check = readlink(head->header.name, linkbuf,
  252.                  (sizeof linkbuf)-1);
  253.  
  254.         if (check < 0) {
  255.             if (errno == ENOENT) {
  256.                 fprintf(msg_file,
  257.                     "%s: no such file or directory\n",
  258.                     head->header.name);
  259.             } else {
  260.                 msg_perror("cannot read link %s",head->header.name);
  261.             }
  262.             different++;
  263.             break;
  264.         }
  265.  
  266.         linkbuf[check] = '\0';    /* Null-terminate it */
  267.         if (strncmp(head->header.linkname, linkbuf, check) != 0) {
  268.             fprintf(msg_file, "%s: symlink differs\n",
  269.                 head->header.linkname);
  270.             different++;
  271.         }
  272.         break;
  273. #endif
  274.  
  275.     case LF_CHR:
  276.         hstat.st_mode |= S_IFCHR;
  277.         goto check_node;
  278.  
  279. #ifdef S_IFBLK
  280.     /* If local system doesn't support block devices, use default case */
  281.     case LF_BLK:
  282.         hstat.st_mode |= S_IFBLK;
  283.         goto check_node;
  284. #endif
  285.  
  286. #ifdef S_IFIFO
  287.     /* If local system doesn't support FIFOs, use default case */
  288.     case LF_FIFO:
  289.         hstat.st_mode |= S_IFIFO;
  290.         hstat.st_rdev = 0;        /* FIXME, do we need this? */
  291.         goto check_node;
  292. #endif
  293.  
  294.     check_node:
  295.         /* FIXME, deal with umask */
  296.         if(do_stat(&filestat))
  297.             break;
  298.         if(hstat.st_rdev != filestat.st_rdev) {
  299.             fprintf(msg_file, "%s: device numbers changed\n", head->header.name);
  300.             different++;
  301.             break;
  302.         }
  303.         if(hstat.st_mode != filestat.st_mode) {
  304.             fprintf(msg_file, "%s: mode or device-type changed\n", head->header.name);
  305.             different++;
  306.             break;
  307.         }
  308.         break;
  309.  
  310.     case LF_DUMPDIR:
  311.         data=diff_dir=get_dir_contents(head->header.name,0);
  312.         wantbytes((long)(hstat.st_size),compare_dir);
  313.         free(data);
  314.         /* FALL THROUGH */
  315.  
  316.     case LF_DIR:
  317.         /* Check for trailing / */
  318.         namelen = strlen(head->header.name)-1;
  319.     really_dir:
  320.         while (namelen && head->header.name[namelen] == '/')
  321.             head->header.name[namelen--] = '\0';    /* Zap / */
  322.  
  323.         if(do_stat(&filestat))
  324.             break;
  325.         if((filestat.st_mode&S_IFMT)!=S_IFDIR) {
  326.             fprintf(msg_file, "%s is no longer a directory\n",head->header.name);
  327.             different++;
  328.             break;
  329.         }
  330.         if((filestat.st_mode&~S_IFMT) != hstat.st_mode)
  331.             sigh("mode");
  332.         break;
  333.  
  334.     case LF_VOLHDR:
  335.         break;
  336.  
  337.     case LF_MULTIVOL:
  338.         namelen = strlen(head->header.name)-1;
  339.         if (head->header.name[namelen] == '/')
  340.             goto really_dir;
  341.  
  342.         if(do_stat(&filestat))
  343.             break;
  344.  
  345.         if ((filestat.st_mode & S_IFMT) != S_IFREG) {
  346.             fprintf(msg_file, "%s: not a regular file\n",
  347.                 head->header.name);
  348.             skip_file((long)hstat.st_size);
  349.             different++;
  350.             break;
  351.         }
  352.  
  353.         filestat.st_mode &= ~S_IFMT;
  354.         offset = from_oct(1+12, head->header.offset);
  355.         if (filestat.st_size != hstat.st_size + offset) {
  356.             sigh("size");
  357.             skip_file((long)hstat.st_size);
  358.             different++;
  359.             break;
  360.         }
  361.  
  362.         diff_fd = open(head->header.name, O_NDELAY|O_RDONLY|O_BINARY);
  363.  
  364.         if (diff_fd < 0) {
  365.             msg_perror("cannot open file %s",head->header.name);
  366.             skip_file((long)hstat.st_size);
  367.             different++;
  368.             break;
  369.         }
  370.         err = lseek(diff_fd, offset, 0);
  371.         if(err!=offset) {
  372.             msg_perror("cannot seek to %ld in file %s",offset,head->header.name);
  373.             different++;
  374.             break;
  375.         }
  376.  
  377.         wantbytes((long)(hstat.st_size),compare_chunk);
  378.  
  379.         check = close(diff_fd);
  380.         if (check < 0) {
  381.             msg_perror("Error while closing %s",head->header.name);
  382.         }
  383.         break;
  384.  
  385.     }
  386.  
  387.     /* We don't need to save it any longer. */
  388.     saverec((union record **) 0);    /* Unsave it */
  389. }
  390.  
  391. int
  392. compare_chunk(bytes,buffer)
  393. long bytes;
  394. char *buffer;
  395. {
  396.     int err;
  397.  
  398.     err=read(diff_fd,diff_buf,bytes);
  399.     if(err!=bytes) {
  400.         if(err<0) {
  401.             msg_perror("can't read %s",head->header.name);
  402.         } else {
  403.             fprintf(msg_file,"%s: could only read %d of %d bytes\n",head->header.name,err,bytes);
  404.         }
  405.         different++;
  406.         return -1;
  407.     }
  408.     if(bcmp(buffer,diff_buf,bytes)) {
  409.         fprintf(msg_file, "%s: data differs\n",head->header.name);
  410.         different++;
  411.         return -1;
  412.     }
  413.     return 0;
  414. }
  415.  
  416. int
  417. compare_dir(bytes,buffer)
  418. long bytes;
  419. char *buffer;
  420. {
  421.     if(bcmp(buffer,diff_dir,bytes)) {
  422.         fprintf(msg_file, "%s: data differs\n",head->header.name);
  423.         different++;
  424.         return -1;
  425.     }
  426.     diff_dir+=bytes;
  427.     return 0;
  428. }
  429.  
  430. /*
  431.  * Sigh about something that differs.
  432.  */
  433. sigh(what)
  434.     char *what;
  435. {
  436.  
  437.     fprintf(msg_file, "%s: %s differs\n",
  438.         head->header.name, what);
  439. }
  440.  
  441. verify_volume()
  442. {
  443.     int status;
  444. #ifdef MTIOCTOP
  445.     struct mtop t;
  446.     int er;
  447. #endif
  448.  
  449.     if(!diff_buf)
  450.         diff_init();
  451. #ifdef MTIOCTOP
  452.     t.mt_op = MTBSF;
  453.     t.mt_count = 1;
  454.     if((er=rmtioctl(archive,MTIOCTOP,&t))<0) {
  455.         if(errno!=EIO || (er=rmtioctl(archive,MTIOCTOP,&t))<0) {
  456. #endif
  457.             if(rmtlseek(archive,0L,0)!=0) {
  458.                 /* Lseek failed.  Try a different method */
  459.                 msg_perror("Couldn't rewind archive file for verify");
  460.                 return;
  461.             }
  462. #ifdef MTIOCTOP
  463.         }
  464.     }
  465. #endif
  466.     ar_reading=1;
  467.     now_verifying = 1;
  468.     fl_read();
  469.     for(;;) {
  470.         status = read_header();
  471.         if(status==0) {
  472.             unsigned n;
  473.  
  474.             n=0;
  475.             do {
  476.                 n++;
  477.                 status=read_header();
  478.             } while(status==0);
  479.             msg("VERIFY FAILURE: %d invalid header%s detected!",n,n==1?"":"s");
  480.         }
  481.         if(status==2 || status==EOF)
  482.             break;
  483.         diff_archive();
  484.     }
  485.     ar_reading=0;
  486.     now_verifying = 0;
  487.  
  488. }
  489.  
  490. int do_stat(statp)
  491. struct stat *statp;
  492. {
  493.     int err;
  494.  
  495.     err = f_follow_links ? stat(head->header.name, statp) : lstat(head->header.name, statp);
  496.     if (err < 0) {
  497.         if (errno==ENOENT) {
  498.             fprintf(msg_file, "%s: does not exist\n",head->header.name);
  499.         } else
  500.             msg_perror("can't stat file %s",head->header.name);
  501. /*        skip_file((long)hstat.st_size);
  502.         different++;*/
  503.         return 1;
  504.     } else
  505.         return 0;
  506. }
  507.  
  508. /*
  509.  * JK
  510.  * Diff'ing a sparse file with its counterpart on the tar file is a
  511.  * bit of a different story than a normal file.  First, we must know
  512.  * what areas of the file to skip through, i.e., we need to contruct
  513.  * a sparsearray, which will hold all the information we need.  We must
  514.  * compare small amounts of data at a time as we find it.
  515.  */
  516.  
  517. diff_sparse_files(filesize)
  518. int    filesize;
  519.  
  520. {
  521.     int        sparse_ind = 0;
  522.     char        *buf;
  523.     int        buf_size = RECORDSIZE;
  524.     union record     *datarec;
  525.     int        err;
  526.     long        numbytes;
  527.     int        amt_read = 0;
  528.     int        size = filesize;
  529.  
  530.     buf = (char *) malloc(buf_size * sizeof (char));
  531.  
  532.     fill_in_sparse_array();
  533.  
  534.  
  535.     while (size > 0) {
  536.         datarec = findrec();
  537.         if (!sparsearray[sparse_ind].numbytes)
  538.             break;
  539.  
  540.         /*
  541.          * 'numbytes' is nicer to write than
  542.          * 'sparsearray[sparse_ind].numbytes' all the time ...
  543.          */
  544.         numbytes = sparsearray[sparse_ind].numbytes;
  545.  
  546.         lseek(diff_fd, sparsearray[sparse_ind].offset, 0);
  547.         /*
  548.          * take care to not run out of room in our buffer
  549.          */
  550.         while (buf_size < numbytes) {
  551.             buf = (char *) realloc(buf, buf_size * 2 * sizeof(char));
  552.             buf_size *= 2;
  553.         }
  554.         while (numbytes > RECORDSIZE) {
  555.             if ((err = read(diff_fd, buf, RECORDSIZE)) != RECORDSIZE) {
  556.                  if (err < 0)
  557.                     msg_perror("can't read %s", head->header.name);
  558.                 else
  559.                     fprintf(msg_file, "%s: could only read %d of %d bytes\n",
  560.                         err, numbytes);
  561.                 break;
  562.             }
  563.             if (bcmp(buf, datarec->charptr, RECORDSIZE)) {
  564.                 different++;
  565.                 break;
  566.             }
  567.             numbytes -= err;
  568.             size -= err;
  569.             userec(datarec);
  570.             datarec = findrec();
  571.         }
  572.         if ((err = read(diff_fd, buf, numbytes)) != numbytes) {
  573.              if (err < 0)
  574.                 msg_perror("can't read %s", head->header.name);
  575.             else
  576.                 fprintf(msg_file, "%s: could only read %d of %d bytes\n",
  577.                         err, numbytes);
  578.             break;
  579.         }
  580.  
  581.         if (bcmp(buf, datarec->charptr, numbytes)) {
  582.             different++;
  583.             break;
  584.         }
  585. /*        amt_read += numbytes;
  586.         if (amt_read >= RECORDSIZE) {
  587.             amt_read = 0;
  588.             userec(datarec);
  589.             datarec = findrec();
  590.         }*/
  591.         userec(datarec);
  592.         sparse_ind++;
  593.         size -= numbytes;
  594.     }
  595.     /*
  596.      * if the number of bytes read isn't the
  597.      * number of bytes supposedly in the file,
  598.      * they're different
  599.      */
  600. /*    if (amt_read != filesize)
  601.         different++;*/
  602.     userec(datarec);
  603.     free(sparsearray);
  604.     if (different)
  605.         fprintf(msg_file, "%s: data differs\n", head->header.name);
  606.  
  607. }
  608.  
  609. /*
  610.  * JK
  611.  * This routine should be used more often than it is ... look into
  612.  * that.  Anyhow, what it does is translate the sparse information
  613.  * on the header, and in any subsequent extended headers, into an
  614.  * array of structures with true numbers, as opposed to character
  615.  * strings.  It simply makes our life much easier, doing so many
  616.  * comparisong and such.
  617.  */
  618. fill_in_sparse_array()
  619. {
  620.     int     ind;
  621.  
  622.     /*
  623.      * allocate space for our scratch space; it's initially
  624.      * 10 elements long, but can change in this routine if
  625.      * necessary
  626.      */
  627.     sp_array_size = 10;
  628.     sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array));
  629.  
  630.     /*
  631.      * there are at most five of these structures in the header
  632.      * itself; read these in first
  633.      */
  634.     for (ind = 0; ind < SPARSE_IN_HDR; ind++) {
  635.         if (!head->header.sp[ind].numbytes)
  636.             break;
  637.         sparsearray[ind].offset =
  638.             from_oct(1+12, head->header.sp[ind].offset);
  639.         sparsearray[ind].numbytes =
  640.             from_oct(1+12, head->header.sp[ind].numbytes);
  641.     }
  642.     /*
  643.      * if the header's extended, we gotta read in exhdr's till
  644.      * we're done
  645.      */
  646.     if (head->header.isextended) {
  647.          /* how far into the sparsearray we are 'so far' */
  648.         static int so_far_ind = SPARSE_IN_HDR;
  649.         union record *exhdr;
  650.  
  651.         for (;;) {
  652.         exhdr = findrec();
  653.         for (ind = 0; ind < SPARSE_EXT_HDR; ind++) {
  654.             if (ind+so_far_ind > sp_array_size-1) {
  655.                 /*
  656.                   * we just ran out of room in our
  657.                  *  scratch area - realloc it
  658.                   */
  659.                 sparsearray = (struct sp_array *)
  660.                     realloc(sparsearray,
  661.                         sp_array_size*2*sizeof(struct sp_array));
  662.                 sp_array_size *= 2;
  663.             }
  664.             /*
  665.              * convert the character strings into longs
  666.              */
  667.             sparsearray[ind+so_far_ind].offset =
  668.                 from_oct(1+12, exhdr->ext_hdr.sp[ind].offset);
  669.             sparsearray[ind+so_far_ind].numbytes =
  670.                 from_oct(1+12, exhdr->ext_hdr.sp[ind].numbytes);
  671.         }
  672.         /*
  673.          * if this is the last extended header for this
  674.          * file, we can stop
  675.          */
  676.         if (!exhdr->ext_hdr.isextended)
  677.             break;
  678.         else {
  679.             so_far_ind += SPARSE_EXT_HDR;
  680.             userec(exhdr);
  681.         }
  682.         }
  683.         /* be sure to skip past the last one  */
  684.         userec(exhdr);
  685.     }
  686. }
  687.