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