cdrestore.c
author nathan
Sat, 29 Dec 2007 15:28:06 +0100
branchtrunk
changeset 8 a306b5e43b44
parent 4 79da91042fcc
child 15 a9348bf5f6e7
permissions -rw-r--r--
release 0.7.0
nathan@0
     1
/* cdrestore.c
nathan@4
     2
Copyright (c) 2000-2004 Craig Condit, Stefan Hülswitt.
nathan@0
     3
nathan@0
     4
Redistribution and use in source and binary forms, with or without
nathan@0
     5
modification, are permitted provided that the following conditions are met: 
nathan@0
     6
nathan@0
     7
1. Redistributions of source code must retain the above copyright notice,
nathan@0
     8
   this list of conditions and the following disclaimer. 
nathan@0
     9
2. Redistributions in binary form must reproduce the above copyright notice,
nathan@0
    10
   this list of conditions and the following disclaimer in the documentation
nathan@0
    11
   and/or other materials provided with the distribution. 
nathan@0
    12
nathan@0
    13
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
nathan@0
    14
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
nathan@0
    15
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
nathan@0
    16
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
nathan@0
    17
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
nathan@0
    18
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
nathan@0
    19
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
nathan@0
    20
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
nathan@0
    21
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
nathan@0
    22
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
nathan@0
    23
SUCH DAMAGE.
nathan@0
    24
*/
nathan@0
    25
nathan@0
    26
#define _LARGEFILE64_SOURCE
nathan@4
    27
#define _GNU_SOURCE
nathan@0
    28
nathan@0
    29
#include <stdio.h>
nathan@0
    30
#include <stdlib.h>
nathan@0
    31
#include <string.h>
nathan@0
    32
#include <unistd.h>
nathan@0
    33
#include <time.h>
nathan@0
    34
#include <errno.h>
nathan@4
    35
#ifndef sun
nathan@0
    36
#include <getopt.h>
nathan@4
    37
#endif
nathan@0
    38
#include <netinet/in.h>
nathan@0
    39
nathan@0
    40
#include "cdbackup.h"
nathan@4
    41
#include "virtual.h"
nathan@0
    42
#include "cdrom.h"
nathan@0
    43
#include "misc.h"
nathan@2
    44
#include "debug.h"
nathan@0
    45
#include "version.h"
nathan@0
    46
nathan@0
    47
/* defaults */
nathan@4
    48
char *prg_name ="cdrestore";
nathan@4
    49
int   cd_track =-1;
nathan@4
    50
char *cd_dev   ="/dev/cdrom";
nathan@4
    51
long  cd_len   =333000; /* blocks */
nathan@4
    52
char *multicmd =0;
nathan@4
    53
int   verbose  =0;
nathan@4
    54
int   force    =0;
nathan@4
    55
int   query    =0;
nathan@4
    56
int   verify   =0;
nathan@4
    57
int   ahead    =0;
nathan@4
    58
int   debug    =0;
nathan@4
    59
int   virtual  =0;
nathan@4
    60
char *virt_name=0;
nathan@0
    61
nathan@0
    62
int tracks;
nathan@2
    63
int disknum;
nathan@0
    64
long long totalSize;
nathan@0
    65
struct header_block headersave;
nathan@0
    66
nathan@0
    67
/****************************************************************************/
nathan@0
    68
nathan@0
    69
void parse_cmdline(char argc, char *argv[]) 
nathan@0
    70
{
nathan@0
    71
  int i;
nathan@0
    72
nathan@4
    73
  while ((i=getopt(argc,argv,"d:l:c:t:qvVFTDRi:"))>0) {
nathan@0
    74
    switch (i) {
nathan@0
    75
       case 'V': fprintf(stderr,"cdrestore "VERSION" (compiled "__DATE__")\n"
nathan@4
    76
	                        "Copyright (C) 2000-2004\n"
nathan@0
    77
			        "This is free software; see the source for copying conditions.\n"
nathan@0
    78
			        "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
nathan@0
    79
			        "PARTICULAR PURPOSE.\n");
nathan@0
    80
	         exit(0);
nathan@0
    81
       case 'c': multicmd=optarg; break;
nathan@0
    82
       case 'd': cd_dev=optarg; break;
nathan@2
    83
       case 'q': query=1; break;
nathan@2
    84
       case 'F': force=1; break;
nathan@2
    85
       case 'R': ahead=1; break;
nathan@2
    86
       case 'T': verify=1; break;
nathan@2
    87
       case 'v': verbose=1; break;
nathan@4
    88
       case 'i': virt_name=optarg; virtual=1; break;
nathan@2
    89
       case 'D': verbose=1; debug=1; 
nathan@2
    90
                 DEBUG("cdrestore: DEBUG output enabled ("VERSION")\n");
nathan@2
    91
                 break;
nathan@0
    92
       case 't': errno=0; cd_track=strtol(optarg,NULL,10);
nathan@0
    93
                 if(errno==ERANGE || cd_track<1) serror("Option -t: invalid track (must be >=1)\n");
nathan@0
    94
	         break;
nathan@4
    95
       case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
nathan@0
    96
	         break;
nathan@4
    97
       default:  fprintf(stderr,
nathan@4
    98
                         "Usage: %s [OPTION]...\n"
nathan@4
    99
                         "Reads block input from CD-R(W) and writes it to standard output.\n\n"
nathan@4
   100
                         "  -d DEVICE      DEVICE for CD queries (e.g. /dev/sr0)\n"
nathan@4
   101
                         "  -q             query disk and print TOC only\n"
nathan@4
   102
                         "  -t N           restore from track N\n"
nathan@4
   103
                         "  -l N           set media size\n"
nathan@4
   104
                         "  -c COMMAND     call COMMAND on disk change in multi-disk mode\n"
nathan@4
   105
                         "  -T             don't restore, test data integrity only\n"
nathan@4
   106
                         "  -F             force starting restore in the middle of a multi-disk set\n"
nathan@4
   107
                         "  -R             set the kernel read-ahead to zero during restore\n"
nathan@4
   108
                         "  -i IMAGE       use virtual image IMAGE for operation\n"
nathan@4
   109
                         "  -v             be verbose\n"
nathan@4
   110
                         "  -D             enable DEBUG output\n"
nathan@4
   111
                         "  -V             prints version & exits\n"
nathan@4
   112
                         "\n", prg_name);
nathan@4
   113
                 exit(0);
nathan@0
   114
       }
nathan@0
   115
    }
nathan@0
   116
nathan@2
   117
  if(!query && cd_track<0) /* need track number */
nathan@0
   118
    serror("A track number is required.\n");
nathan@0
   119
}
nathan@0
   120
nathan@0
   121
/****************************************************************************/
nathan@0
   122
nathan@4
   123
void print_track(int track, char *stamp, char *id, int disk, int startsec, int endsec, char flags)
nathan@4
   124
{
nathan@4
   125
  char timestr[32], size[32], flstr[12];
nathan@4
   126
nathan@4
   127
  snprintf(timestr,sizeof(timestr),"%02d/%02d/%04d %02d:%02d",
nathan@0
   128
    (stamp[4]-'0')*10   + (stamp[5]-'0'), 
nathan@0
   129
    (stamp[6]-'0')*10   + (stamp[7]-'0'),
nathan@0
   130
    (stamp[0]-'0')*1000 + (stamp[1]-'0')*100 + (stamp[2]-'0')*10 + (stamp[3]-'0'),
nathan@0
   131
    (stamp[8]-'0')*10   + (stamp[9]-'0'),
nathan@0
   132
    (stamp[10]-'0')*10  + (stamp[11]-'0'));
nathan@0
   133
nathan@4
   134
  if(startsec>=0) snprintf(size,sizeof(size)," %s:",FlexSize(flstr,((long long)(endsec-startsec+1)*CD_FRAMESIZE)));
nathan@0
   135
  else size[0]=0;
nathan@4
   136
  snprintf(flstr,sizeof(flstr),"%c",flags&F_CRC?'C':'.');
nathan@4
   137
nathan@4
   138
  fprintf(stderr,"Track %02d:%s %s Part %d %s : %s\n", track, size, timestr, disk, flstr, id);
nathan@2
   139
  if(startsec>=0) DEBUG("          Start sector %7d Last sector %7d\n",startsec,endsec);
nathan@2
   140
}
nathan@2
   141
nathan@2
   142
/****************************************************************************/
nathan@2
   143
nathan@2
   144
int restore(int disktrack)
nathan@0
   145
{
nathan@0
   146
  int result=0, i, bytes;
nathan@4
   147
  long long totalRead=0, startPos;
nathan@0
   148
  struct header_block header;
nathan@0
   149
  char buffer[CD_FRAMESIZE];
nathan@0
   150
  struct data_block *db=(struct data_block *)&buffer[0];
nathan@4
   151
nathan@4
   152
  for(i=tracks-1; i>=0; i--) if(toc[i].track_no==disktrack) break;
nathan@4
   153
  if(i<0) { fprintf(stderr, "%s: Can't find track %d\n", prg_name, disktrack); exit(1); }
nathan@4
   154
  startPos=Vseek(i);
nathan@4
   155
nathan@4
   156
  Vread(buffer); totalRead+=CD_FRAMESIZE;
nathan@0
   157
  memcpy(&header,buffer,sizeof(header));
nathan@0
   158
  if(!strncmp(SHORT_HDR,header.id_str,strlen(SHORT_HDR))) {
nathan@2
   159
    if(verbose) {
nathan@2
   160
      fprintf(stderr,"%s: ", prg_name);
nathan@4
   161
      print_track(disktrack, header.t_stamp, header.vol_id, header.disk_set, -1, -1, header.flags);
nathan@2
   162
      }
nathan@0
   163
nathan@0
   164
    if(disknum==1) {
nathan@0
   165
      if(header.disk_set!=1) {
nathan@2
   166
        if(!force) {
nathan@2
   167
          fprintf(stderr,"%s: This is disk %d of the multi-disk set! Use -F if you really want to start with this disk.\n",prg_name,header.disk_set);
nathan@2
   168
          exit(1);
nathan@2
   169
          }
nathan@2
   170
        fprintf(stderr,"%s: This is disk %d of the multi-disk set, but -F forces me to continue!\n",prg_name,header.disk_set);
nathan@2
   171
        disknum=header.disk_set;
nathan@0
   172
        }
nathan@0
   173
      headersave=header;		/* save header for use with disk 2-n */
nathan@0
   174
      }
nathan@0
   175
    else {
nathan@0
   176
      if(strcmp(header.t_stamp,headersave.t_stamp) || strcmp(header.vol_id,headersave.vol_id)) {
nathan@4
   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);
nathan@2
   178
        result=-1;
nathan@0
   179
        }
nathan@0
   180
      else if(header.disk_set!=disknum) {
nathan@4
   181
        fprintf(stderr,"%s: Wrong sequence. This is disk %d, but you need disk %d now!\n",prg_name,header.disk_set,disknum);
nathan@2
   182
        result=-1;
nathan@0
   183
        }
nathan@2
   184
      else if(verbose) fprintf(stderr, "%s: Beginning restore (Disk %d)\n", prg_name,disknum);
nathan@0
   185
      }
nathan@0
   186
    }
nathan@0
   187
  else {
nathan@0
   188
    fprintf(stderr, "%s: Track %02d was not created with 'cdbackup'\n", prg_name,disktrack);
nathan@0
   189
    if(disknum==1) exit(1);
nathan@2
   190
    result=-1;
nathan@0
   191
    }
nathan@0
   192
nathan@0
   193
  while(!result) {
nathan@0
   194
    int size;
nathan@0
   195
nathan@2
   196
    DEBUG("\rReading sector %7ld  ",(long)((startPos+totalRead)/CD_FRAMESIZE));
nathan@4
   197
    Vread(buffer);
nathan@4
   198
nathan@0
   199
    size=ntohs(db->datasize);
nathan@0
   200
    if(size>DATASIZE) {
nathan@2
   201
      if(verbose) fprintf(stderr,"%s: Warning! Bad datasize at %lld\n",prg_name,totalRead);
nathan@0
   202
      size=DATASIZE;
nathan@0
   203
      }
nathan@0
   204
nathan@4
   205
    if(db->flags&F_CRC) {
nathan@4
   206
      int l=crc32(buffer,size+DBSIZE);
nathan@4
   207
      if(*((unsigned long *)(&buffer[CD_FRAMESIZE-4]))!=l) {
nathan@4
   208
        if(verbose) fprintf(stderr,"%s: bad CRC checksum at %lld\n",prg_name,totalRead);
nathan@4
   209
        serror("Bad checksum, block corrupted, restore failed");
nathan@4
   210
        }
nathan@4
   211
      }
nathan@4
   212
nathan@4
   213
    totalRead+=CD_FRAMESIZE;
nathan@4
   214
nathan@2
   215
    if(!verify) {
nathan@4
   216
      bytes=write(1,&buffer[DBSIZE],size);
nathan@4
   217
      if(bytes!=size) error("Write failed (stdout)");
nathan@2
   218
      }
nathan@0
   219
nathan@0
   220
    if(db->status == 1) break; 	  /* end of backup*/
nathan@2
   221
    if(db->status == 2) result=1; /* next disk */
nathan@2
   222
    }
nathan@2
   223
  DEBUG("\n");
nathan@0
   224
nathan@0
   225
  /* print status */
nathan@0
   226
  totalSize+=totalRead;
nathan@4
   227
  if(result>=0 && verbose) {
nathan@4
   228
    char str1[16], str2[16];
nathan@4
   229
    fprintf(stderr, "%s: Restore complete. %s read (%s from this disk)\n",
nathan@4
   230
            prg_name,FlexSize(str1,totalSize),FlexSize(str2,totalRead));
nathan@4
   231
    }
nathan@4
   232
nathan@0
   233
  return(result);
nathan@0
   234
}
nathan@0
   235
nathan@0
   236
/****************************************************************************/
nathan@0
   237
nathan@0
   238
void print_toc()
nathan@0
   239
{
nathan@0
   240
  int i;
nathan@0
   241
nathan@0
   242
  fprintf(stderr,"Tracks: %d\n",tracks);
nathan@4
   243
  VprintSpace();
nathan@0
   244
  fprintf(stderr,"\n");
nathan@0
   245
  
nathan@4
   246
  for(i=0; i<tracks; i++) {
nathan@4
   247
    if(!toc[i].is_data)
nathan@4
   248
      fprintf(stderr,"Track %02d: Non-data\n",toc[i].track_no);
nathan@4
   249
    else if(toc[i].is_cdbackup)
nathan@4
   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);
nathan@4
   251
    else
nathan@4
   252
      fprintf(stderr,"Track %02d: Data\n", toc[i].track_no);
nathan@0
   253
    }
nathan@0
   254
}
nathan@0
   255
nathan@0
   256
/****************************************************************************/
nathan@0
   257
nathan@0
   258
int main(int argc, char *argv[])
nathan@0
   259
{
nathan@0
   260
  parse_cmdline(argc, argv);
nathan@0
   261
nathan@4
   262
  disknum=1; totalSize=0;
nathan@4
   263
  Vopen(1); tracks=VreadToc(query || debug);
nathan@2
   264
  if(query || debug) {
nathan@2
   265
    verbose=1;
nathan@2
   266
    print_toc();
nathan@2
   267
    }
nathan@2
   268
  if(!query) {
nathan@2
   269
    int result;
nathan@2
   270
    if(verify) fprintf(stderr,"%s: Verify mode enabled, no data output!\n",prg_name);
nathan@4
   271
    if(ahead) { VgetAhead(); VsetAhead(0); }
nathan@0
   272
    do {
nathan@2
   273
      result=restore(cd_track);
nathan@0
   274
      if(result) {
nathan@2
   275
        if(result>0) { disknum++; cd_track=1; }
nathan@4
   276
        Vclose();
nathan@4
   277
        if(!VisRegular()) {
nathan@4
   278
          fprintf(stderr,"%s: Next disk needed: disk %d from %s\n",prg_name,disknum,headersave.vol_id);
nathan@4
   279
          diskchange(multicmd,cd_dev);
nathan@4
   280
          }
nathan@4
   281
        else if(result<0) break;
nathan@4
   282
        Vopen(1); tracks=VreadToc(0);
nathan@0
   283
	}
nathan@0
   284
      } while(result);
nathan@4
   285
    if(ahead) VsetAhead(1);
nathan@4
   286
    }
nathan@4
   287
  Vclose();
nathan@0
   288
  return 0;
nathan@0
   289
}