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