cdrestore.c
branchtrunk
changeset 4 79da91042fcc
parent 2 6bcb44b9edb1
child 15 a9348bf5f6e7
equal deleted inserted replaced
3:d09ec85ffdfe 4:79da91042fcc
     1 /* cdrestore.c
     1 /* cdrestore.c
     2 Copyright (c) 2000-2002 Craig Condit, Stefan Hülswitt.
     2 Copyright (c) 2000-2004 Craig Condit, Stefan Hülswitt.
     3 
     3 
     4 Redistribution and use in source and binary forms, with or without
     4 Redistribution and use in source and binary forms, with or without
     5 modification, are permitted provided that the following conditions are met: 
     5 modification, are permitted provided that the following conditions are met: 
     6 
     6 
     7 1. Redistributions of source code must retain the above copyright notice,
     7 1. Redistributions of source code must retain the above copyright notice,
    22 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    22 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    23 SUCH DAMAGE.
    23 SUCH DAMAGE.
    24 */
    24 */
    25 
    25 
    26 #define _LARGEFILE64_SOURCE
    26 #define _LARGEFILE64_SOURCE
       
    27 #define _GNU_SOURCE
    27 
    28 
    28 #include <stdio.h>
    29 #include <stdio.h>
    29 #include <stdlib.h>
    30 #include <stdlib.h>
    30 #include <string.h>
    31 #include <string.h>
    31 #include <unistd.h>
    32 #include <unistd.h>
    32 #include <fcntl.h>
       
    33 #include <time.h>
    33 #include <time.h>
    34 #include <errno.h>
    34 #include <errno.h>
       
    35 #ifndef sun
    35 #include <getopt.h>
    36 #include <getopt.h>
    36 #include <sys/ioctl.h>
    37 #endif
    37 #include <sys/wait.h>
       
    38 #include <netinet/in.h>
    38 #include <netinet/in.h>
    39 #include <linux/cdrom.h>
       
    40 
    39 
    41 #include "cdbackup.h"
    40 #include "cdbackup.h"
       
    41 #include "virtual.h"
    42 #include "cdrom.h"
    42 #include "cdrom.h"
    43 #include "misc.h"
    43 #include "misc.h"
    44 #include "debug.h"
    44 #include "debug.h"
    45 #include "version.h"
    45 #include "version.h"
    46 
    46 
    47 /* defaults */
    47 /* defaults */
    48 char * prg_name ="cdrestore";
    48 char *prg_name ="cdrestore";
    49 int    cd_track =-1;
    49 int   cd_track =-1;
    50 char * cd_dev   ="/dev/cdrom";
    50 char *cd_dev   ="/dev/cdrom";
    51 long   cd_len   =333000; /* blocks */
    51 long  cd_len   =333000; /* blocks */
    52 char * multicmd =0;
    52 char *multicmd =0;
    53 int    verbose  =0;
    53 int   verbose  =0;
    54 int    force    =0;
    54 int   force    =0;
    55 int    query    =0;
    55 int   query    =0;
    56 int    verify   =0;
    56 int   verify   =0;
    57 int    ahead    =0;
    57 int   ahead    =0;
    58 int    debug    =0;
    58 int   debug    =0;
       
    59 int   virtual  =0;
       
    60 char *virt_name=0;
    59 
    61 
    60 int tracks;
    62 int tracks;
    61 int disknum;
    63 int disknum;
    62 long long totalSize;
    64 long long totalSize;
    63 struct header_block headersave;
    65 struct header_block headersave;
    64 
    66 
    65 /****************************************************************************/
    67 /****************************************************************************/
    66 
    68 
    67 void usage()
       
    68 {
       
    69   fprintf(stderr,
       
    70     "Usage: %s [OPTION]...\n"
       
    71     "Reads block input from CD-R(W) and writes it to standard output.\n\n"
       
    72     "  -d DEVICE      DEVICE for CD queries (e.g. /dev/sr0)\n"
       
    73     "  -q             query disk and print TOC only\n"
       
    74     "  -t N           restore from track N\n"
       
    75     "  -l N           CD-R has a size of N MB\n"
       
    76     "  -c COMMAND     call COMMAND on disk change in multi-disk mode\n"
       
    77     "  -T             don't restore, test data integrity only\n"
       
    78     "  -F             force starting restore in the middle of a multi-disk set\n"
       
    79     "  -R             set the kernel read-ahead to zero during restore\n"
       
    80     "  -v             be verbose\n"
       
    81     "  -D             enable DEBUG output\n"
       
    82     "  -V             prints version & exits\n"
       
    83     "\n", prg_name);
       
    84 }
       
    85 
       
    86 /****************************************************************************/
       
    87 
       
    88 void parse_cmdline(char argc, char *argv[]) 
    69 void parse_cmdline(char argc, char *argv[]) 
    89 {
    70 {
    90   int i;
    71   int i;
    91 
    72 
    92   while ((i=getopt(argc,argv,"d:l:c:t:qvVFTDR"))>0) {
    73   while ((i=getopt(argc,argv,"d:l:c:t:qvVFTDRi:"))>0) {
    93     switch (i) {
    74     switch (i) {
    94        case 'V': fprintf(stderr,"cdrestore "VERSION" (compiled "__DATE__")\n"
    75        case 'V': fprintf(stderr,"cdrestore "VERSION" (compiled "__DATE__")\n"
    95 	                        "Copyright (C) 2000-2002\n"
    76 	                        "Copyright (C) 2000-2004\n"
    96 			        "This is free software; see the source for copying conditions.\n"
    77 			        "This is free software; see the source for copying conditions.\n"
    97 			        "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
    78 			        "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
    98 			        "PARTICULAR PURPOSE.\n");
    79 			        "PARTICULAR PURPOSE.\n");
    99 	         exit(0);
    80 	         exit(0);
   100        case 'c': multicmd=optarg; break;
    81        case 'c': multicmd=optarg; break;
   102        case 'q': query=1; break;
    83        case 'q': query=1; break;
   103        case 'F': force=1; break;
    84        case 'F': force=1; break;
   104        case 'R': ahead=1; break;
    85        case 'R': ahead=1; break;
   105        case 'T': verify=1; break;
    86        case 'T': verify=1; break;
   106        case 'v': verbose=1; break;
    87        case 'v': verbose=1; break;
       
    88        case 'i': virt_name=optarg; virtual=1; break;
   107        case 'D': verbose=1; debug=1; 
    89        case 'D': verbose=1; debug=1; 
   108                  DEBUG("cdrestore: DEBUG output enabled ("VERSION")\n");
    90                  DEBUG("cdrestore: DEBUG output enabled ("VERSION")\n");
   109                  break;
    91                  break;
   110        case 't': errno=0; cd_track=strtol(optarg,NULL,10);
    92        case 't': errno=0; cd_track=strtol(optarg,NULL,10);
   111                  if(errno==ERANGE || cd_track<1) serror("Option -t: invalid track (must be >=1)\n");
    93                  if(errno==ERANGE || cd_track<1) serror("Option -t: invalid track (must be >=1)\n");
   112 	         break;
    94 	         break;
   113        case 'l': errno=0; cd_len=strtol(optarg,NULL,10);
    95        case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
   114                  if(errno==ERANGE || cd_len<1) serror("Option -l: length out of range (must be >=1)\n");
       
   115 	         cd_len = (long long)cd_len * (1024*1024) / CD_FRAMESIZE; /* convert to blocks */
       
   116 	         break;
    96 	         break;
   117        default:  usage(); exit(0);
    97        default:  fprintf(stderr,
       
    98                          "Usage: %s [OPTION]...\n"
       
    99                          "Reads block input from CD-R(W) and writes it to standard output.\n\n"
       
   100                          "  -d DEVICE      DEVICE for CD queries (e.g. /dev/sr0)\n"
       
   101                          "  -q             query disk and print TOC only\n"
       
   102                          "  -t N           restore from track N\n"
       
   103                          "  -l N           set media size\n"
       
   104                          "  -c COMMAND     call COMMAND on disk change in multi-disk mode\n"
       
   105                          "  -T             don't restore, test data integrity only\n"
       
   106                          "  -F             force starting restore in the middle of a multi-disk set\n"
       
   107                          "  -R             set the kernel read-ahead to zero during restore\n"
       
   108                          "  -i IMAGE       use virtual image IMAGE for operation\n"
       
   109                          "  -v             be verbose\n"
       
   110                          "  -D             enable DEBUG output\n"
       
   111                          "  -V             prints version & exits\n"
       
   112                          "\n", prg_name);
       
   113                  exit(0);
   118        }
   114        }
   119     }
   115     }
   120 
   116 
   121   if(!query && cd_track<0) /* need track number */
   117   if(!query && cd_track<0) /* need track number */
   122     serror("A track number is required.\n");
   118     serror("A track number is required.\n");
   123 }
   119 }
   124 
   120 
   125 /****************************************************************************/
   121 /****************************************************************************/
   126 
   122 
   127 void print_track(int track, char *stamp, char *id, int disk, int startsec, int endsec)
   123 void print_track(int track, char *stamp, char *id, int disk, int startsec, int endsec, char flags)
   128 {
   124 {
   129   char timestr[32], size[32];
   125   char timestr[32], size[32], flstr[12];
   130 
   126 
   131   snprintf(timestr,sizeof(timestr)-1,"%02d/%02d/%04d %02d:%02d",
   127   snprintf(timestr,sizeof(timestr),"%02d/%02d/%04d %02d:%02d",
   132     (stamp[4]-'0')*10   + (stamp[5]-'0'), 
   128     (stamp[4]-'0')*10   + (stamp[5]-'0'), 
   133     (stamp[6]-'0')*10   + (stamp[7]-'0'),
   129     (stamp[6]-'0')*10   + (stamp[7]-'0'),
   134     (stamp[0]-'0')*1000 + (stamp[1]-'0')*100 + (stamp[2]-'0')*10 + (stamp[3]-'0'),
   130     (stamp[0]-'0')*1000 + (stamp[1]-'0')*100 + (stamp[2]-'0')*10 + (stamp[3]-'0'),
   135     (stamp[8]-'0')*10   + (stamp[9]-'0'),
   131     (stamp[8]-'0')*10   + (stamp[9]-'0'),
   136     (stamp[10]-'0')*10  + (stamp[11]-'0'));
   132     (stamp[10]-'0')*10  + (stamp[11]-'0'));
   137 
   133 
   138   if(startsec>=0) snprintf(size,sizeof(size)-1," %3ld MB:",(long)((long long)(endsec-startsec+1)*CD_FRAMESIZE/(1024*1024)));
   134   if(startsec>=0) snprintf(size,sizeof(size)," %s:",FlexSize(flstr,((long long)(endsec-startsec+1)*CD_FRAMESIZE)));
   139   else size[0]=0;
   135   else size[0]=0;
   140 
   136   snprintf(flstr,sizeof(flstr),"%c",flags&F_CRC?'C':'.');
   141   fprintf(stderr,"Track %02d:%s %s  Part %d: %s\n", track, size, timestr, disk, id);
   137 
       
   138   fprintf(stderr,"Track %02d:%s %s Part %d %s : %s\n", track, size, timestr, disk, flstr, id);
   142   if(startsec>=0) DEBUG("          Start sector %7d Last sector %7d\n",startsec,endsec);
   139   if(startsec>=0) DEBUG("          Start sector %7d Last sector %7d\n",startsec,endsec);
   143 }
   140 }
   144 
   141 
   145 /****************************************************************************/
   142 /****************************************************************************/
   146 
   143 
   147 int restore(int disktrack)
   144 int restore(int disktrack)
   148 {
   145 {
   149   int infile;
       
   150   int result=0, i, bytes;
   146   int result=0, i, bytes;
   151   long long totalRead, startPos;
   147   long long totalRead=0, startPos;
   152   struct header_block header;
   148   struct header_block header;
   153   char buffer[CD_FRAMESIZE];
   149   char buffer[CD_FRAMESIZE];
   154   struct data_block *db=(struct data_block *)&buffer[0];
   150   struct data_block *db=(struct data_block *)&buffer[0];
   155   unsigned long r_ahead, r_fahead;
   151 
   156 
   152   for(i=tracks-1; i>=0; i--) if(toc[i].track_no==disktrack) break;
   157   if((infile=open(cd_dev, O_RDONLY)) < 0) error("Error opening device");
   153   if(i<0) { fprintf(stderr, "%s: Can't find track %d\n", prg_name, disktrack); exit(1); }
   158   if(ahead) {
   154   startPos=Vseek(i);
   159     get_param(infile,&r_ahead,&r_fahead);
   155 
   160     set_param(infile,0,0);
   156   Vread(buffer); totalRead+=CD_FRAMESIZE;
   161     }
       
   162 
       
   163   /* seek to proper CD-R(W) track */
       
   164   for(i=tracks;i>0;i--) if(toc[i].track_no==disktrack) break;
       
   165   if(!i) { fprintf(stderr, "%s: Can't find track %d\n", prg_name, disktrack); exit(1); }
       
   166 
       
   167   startPos=(long long)toc[i].sec_start*CD_FRAMESIZE;
       
   168   if(lseek64(infile,startPos,SEEK_SET) != startPos) error("Error seeking to track");
       
   169 
       
   170   /* read header block */
       
   171   bytes=full_read(infile,buffer,CD_FRAMESIZE);
       
   172   if (bytes < 0) error("Error reading header block");
       
   173   if (bytes != CD_FRAMESIZE) error("Unexpected EOF reading header block");
       
   174   totalRead = bytes;
       
   175 
       
   176   memcpy(&header,buffer,sizeof(header));
   157   memcpy(&header,buffer,sizeof(header));
   177 
       
   178   if(!strncmp(SHORT_HDR,header.id_str,strlen(SHORT_HDR))) {
   158   if(!strncmp(SHORT_HDR,header.id_str,strlen(SHORT_HDR))) {
   179     if(verbose) {
   159     if(verbose) {
   180       fprintf(stderr,"%s: ", prg_name);
   160       fprintf(stderr,"%s: ", prg_name);
   181       print_track(disktrack, header.t_stamp, header.vol_id, header.disk_set, -1, -1);
   161       print_track(disktrack, header.t_stamp, header.vol_id, header.disk_set, -1, -1, header.flags);
   182       }
   162       }
   183 
   163 
   184     if(disknum==1) {
   164     if(disknum==1) {
   185       if(header.disk_set!=1) {
   165       if(header.disk_set!=1) {
   186         if(!force) {
   166         if(!force) {
   192         }
   172         }
   193       headersave=header;		/* save header for use with disk 2-n */
   173       headersave=header;		/* save header for use with disk 2-n */
   194       }
   174       }
   195     else {
   175     else {
   196       if(strcmp(header.t_stamp,headersave.t_stamp) || strcmp(header.vol_id,headersave.vol_id)) {
   176       if(strcmp(header.t_stamp,headersave.t_stamp) || strcmp(header.vol_id,headersave.vol_id)) {
   197         fprintf(stderr,"%s: This disk doesn't belong to the current set!\n",prg_name);
   177         fprintf(stderr,"%s: This disk belongs to the backup set '%s', but you're restoring set '%s'!\n",prg_name,header.vol_id,headersave.vol_id);
   198         result=-1;
   178         result=-1;
   199         }
   179         }
   200       else if(header.disk_set!=disknum) {
   180       else if(header.disk_set!=disknum) {
   201         fprintf(stderr,"%s: Wrong sequence. You need disk %d now!\n",prg_name,disknum);
   181         fprintf(stderr,"%s: Wrong sequence. This is disk %d, but you need disk %d now!\n",prg_name,header.disk_set,disknum);
   202         result=-1;
   182         result=-1;
   203         }
   183         }
   204       else if(verbose) fprintf(stderr, "%s: Beginning restore (Disk %d)\n", prg_name,disknum);
   184       else if(verbose) fprintf(stderr, "%s: Beginning restore (Disk %d)\n", prg_name,disknum);
   205       }
   185       }
   206     }
   186     }
   211     }
   191     }
   212 
   192 
   213   while(!result) {
   193   while(!result) {
   214     int size;
   194     int size;
   215 
   195 
   216     /* read data block */
       
   217     DEBUG("\rReading sector %7ld  ",(long)((startPos+totalRead)/CD_FRAMESIZE));
   196     DEBUG("\rReading sector %7ld  ",(long)((startPos+totalRead)/CD_FRAMESIZE));
   218     bytes = full_read(infile, buffer, CD_FRAMESIZE);
   197     Vread(buffer);
   219     if (bytes < 0) error("Error reading data");
   198 
   220     if (bytes != CD_FRAMESIZE) error("Unexpected EOF reading data");
       
   221     totalRead += bytes;
       
   222 
       
   223     /* sanity check */
       
   224     size=ntohs(db->datasize);
   199     size=ntohs(db->datasize);
   225     if(size>DATASIZE) {
   200     if(size>DATASIZE) {
   226       if(verbose) fprintf(stderr,"%s: Warning! Bad datasize at %lld\n",prg_name,totalRead);
   201       if(verbose) fprintf(stderr,"%s: Warning! Bad datasize at %lld\n",prg_name,totalRead);
   227       size=DATASIZE;
   202       size=DATASIZE;
   228       }
   203       }
   229 
   204 
       
   205     if(db->flags&F_CRC) {
       
   206       int l=crc32(buffer,size+DBSIZE);
       
   207       if(*((unsigned long *)(&buffer[CD_FRAMESIZE-4]))!=l) {
       
   208         if(verbose) fprintf(stderr,"%s: bad CRC checksum at %lld\n",prg_name,totalRead);
       
   209         serror("Bad checksum, block corrupted, restore failed");
       
   210         }
       
   211       }
       
   212 
       
   213     totalRead+=CD_FRAMESIZE;
       
   214 
   230     if(!verify) {
   215     if(!verify) {
   231       /* write the data block */
   216       bytes=write(1,&buffer[DBSIZE],size);
   232       bytes=write(1,&buffer[DBSIZE], size);
   217       if(bytes!=size) error("Write failed (stdout)");
   233       if(bytes!=size) error("Error writing data");
       
   234       }
   218       }
   235 
   219 
   236     if(db->status == 1) break; 	  /* end of backup*/
   220     if(db->status == 1) break; 	  /* end of backup*/
   237     if(db->status == 2) result=1; /* next disk */
   221     if(db->status == 2) result=1; /* next disk */
   238     }
   222     }
   239   DEBUG("\n");
   223   DEBUG("\n");
   240 
   224 
   241   /* print status */
   225   /* print status */
   242   totalSize+=totalRead;
   226   totalSize+=totalRead;
   243   if(result>=0 && verbose)
   227   if(result>=0 && verbose) {
   244     fprintf(stderr, "%s: Restore complete. %lld kB read (%lld kB from this disk)\n",prg_name, totalSize/1024, totalRead/1024);
   228     char str1[16], str2[16];
   245 
   229     fprintf(stderr, "%s: Restore complete. %s read (%s from this disk)\n",
   246   if(ahead) {
   230             prg_name,FlexSize(str1,totalSize),FlexSize(str2,totalRead));
   247     set_param(infile,r_ahead,r_fahead);
   231     }
   248     get_param(infile,&r_ahead,&r_fahead);
   232 
   249     }
       
   250   close(infile);
       
   251   return(result);
   233   return(result);
   252 }
   234 }
   253 
   235 
   254 /****************************************************************************/
   236 /****************************************************************************/
   255 
   237 
   256 void print_toc()
   238 void print_toc()
   257 {
   239 {
   258   int i;
   240   int i;
   259 
   241 
   260   fprintf(stderr,"Tracks: %d\n",tracks);
   242   fprintf(stderr,"Tracks: %d\n",tracks);
   261   print_space();
   243   VprintSpace();
   262   fprintf(stderr,"\n");
   244   fprintf(stderr,"\n");
   263   
   245   
   264   for (i = 1; i <= tracks; i++) {
   246   for(i=0; i<tracks; i++) {
   265     if(toc[i].is_data==0) fprintf(stderr,"Track %02d: Non-data\n", toc[i].track_no);
   247     if(!toc[i].is_data)
   266     else if (toc[i].is_cdbackup == 1)
   248       fprintf(stderr,"Track %02d: Non-data\n",toc[i].track_no);
   267       print_track(i, toc[i].t_stamp, toc[i].vol_id, toc[i].disk_set, toc[i].sec_start, toc[i].sec_end);
   249     else if(toc[i].is_cdbackup)
   268     else fprintf(stderr,"Track %02d: Data\n", toc[i].track_no);
   250       print_track(toc[i].track_no,toc[i].t_stamp,toc[i].vol_id,toc[i].disk_set,toc[i].sec_start,toc[i].sec_end,toc[i].flags);
       
   251     else
       
   252       fprintf(stderr,"Track %02d: Data\n", toc[i].track_no);
   269     }
   253     }
   270 }
   254 }
   271 
   255 
   272 /****************************************************************************/
   256 /****************************************************************************/
   273 
   257 
   274 int main(int argc, char *argv[])
   258 int main(int argc, char *argv[])
   275 {
   259 {
   276   int cdr;
       
   277 
       
   278   parse_cmdline(argc, argv);
   260   parse_cmdline(argc, argv);
   279 
   261 
   280   cdr=open_cdr(cd_dev); tracks=read_toc(cdr,(query || debug)); close_cdr(cdr);
   262   disknum=1; totalSize=0;
       
   263   Vopen(1); tracks=VreadToc(query || debug);
   281   if(query || debug) {
   264   if(query || debug) {
   282     verbose=1;
   265     verbose=1;
   283     print_toc();
   266     print_toc();
   284     }
   267     }
   285   if(!query) {
   268   if(!query) {
   286     int result;
   269     int result;
   287     if(verify) fprintf(stderr,"%s: Verify mode enabled, no data output!\n",prg_name);
   270     if(verify) fprintf(stderr,"%s: Verify mode enabled, no data output!\n",prg_name);
   288     totalSize=0; disknum=1;
   271     if(ahead) { VgetAhead(); VsetAhead(0); }
   289     do {
   272     do {
   290       result=restore(cd_track);
   273       result=restore(cd_track);
   291       if(result) {
   274       if(result) {
   292         if(result>0) { disknum++; cd_track=1; }
   275         if(result>0) { disknum++; cd_track=1; }
   293         fprintf(stderr,"%s: Next disk needed: disk %d from %s\n",prg_name,disknum,headersave.vol_id);
   276         Vclose();
   294         if(start_diskchange(multicmd,cd_dev)) serror("Diskchange command failed");
   277         if(!VisRegular()) {
       
   278           fprintf(stderr,"%s: Next disk needed: disk %d from %s\n",prg_name,disknum,headersave.vol_id);
       
   279           diskchange(multicmd,cd_dev);
       
   280           }
       
   281         else if(result<0) break;
       
   282         Vopen(1); tracks=VreadToc(0);
   295 	}
   283 	}
   296       } while(result);
   284       } while(result);
   297     }
   285     if(ahead) VsetAhead(1);
       
   286     }
       
   287   Vclose();
   298   return 0;
   288   return 0;
   299 }
   289 }