nathan@0: /* cdrestore.c nathan@0: Copyright (c) 2000-2002 Craig Condit, Stefan Hülswitt. nathan@0: nathan@0: Redistribution and use in source and binary forms, with or without nathan@0: modification, are permitted provided that the following conditions are met: nathan@0: nathan@0: 1. Redistributions of source code must retain the above copyright notice, nathan@0: this list of conditions and the following disclaimer. nathan@0: 2. Redistributions in binary form must reproduce the above copyright notice, nathan@0: this list of conditions and the following disclaimer in the documentation nathan@0: and/or other materials provided with the distribution. nathan@0: nathan@0: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS nathan@0: OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED nathan@0: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE nathan@0: DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR nathan@0: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL nathan@0: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR nathan@0: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER nathan@0: CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT nathan@0: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY nathan@0: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF nathan@0: SUCH DAMAGE. nathan@0: */ nathan@0: nathan@0: #define _LARGEFILE64_SOURCE nathan@0: nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: nathan@0: #include "cdbackup.h" nathan@0: #include "cdrom.h" nathan@0: #include "misc.h" nathan@0: #include "version.h" nathan@0: nathan@0: /* defaults */ nathan@0: char * prg_name ="cdrestore"; nathan@0: int cd_mode =0; nathan@0: int cd_track =-1; nathan@0: char * cd_dev ="/dev/cdrom"; nathan@0: long cd_len =333000; /* blocks */ nathan@0: char * multicmd =0; nathan@0: int verbose =1; nathan@0: nathan@0: int tracks; nathan@0: long long totalSize; nathan@0: struct header_block headersave; nathan@0: nathan@0: /****************************************************************************/ nathan@0: nathan@0: void usage() nathan@0: { nathan@0: fprintf(stderr, nathan@0: "Usage: %s [OPTION]...\n" nathan@0: "Reads block input from CD-R(W) and writes it to standard output.\n\n" nathan@0: " -d DEVICE DEVICE for CD queries (e.g. /dev/sr0)\n" nathan@0: " -q query disk and print TOC only\n" nathan@0: " -t N restore from track N\n" nathan@0: " -l N CD-R has a size of N MB\n" nathan@0: " -c COMMAND call COMMAND on disk change in multi-disk mode\n" nathan@0: " -V prints version & exits\n" nathan@0: "\n", prg_name); nathan@0: } nathan@0: nathan@0: /****************************************************************************/ nathan@0: nathan@0: void parse_cmdline(char argc, char *argv[]) nathan@0: { nathan@0: int i; nathan@0: nathan@0: while ((i=getopt(argc,argv,"d:l:c:t:qV"))>0) { nathan@0: switch (i) { nathan@0: case 'V': fprintf(stderr,"cdrestore "VERSION" (compiled "__DATE__")\n" nathan@0: "Copyright (C) 2000-2002\n" nathan@0: "This is free software; see the source for copying conditions.\n" nathan@0: "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n" nathan@0: "PARTICULAR PURPOSE.\n"); nathan@0: exit(0); nathan@0: case 'c': multicmd=optarg; break; nathan@0: case 'd': cd_dev=optarg; break; nathan@0: case 'q': cd_mode=1; break; nathan@0: case 't': errno=0; cd_track=strtol(optarg,NULL,10); nathan@0: if(errno==ERANGE || cd_track<1) serror("Option -t: invalid track (must be >=1)\n"); nathan@0: break; nathan@0: case 'l': errno=0; cd_len=strtol(optarg,NULL,10); nathan@0: if(errno==ERANGE || cd_len<1) serror("Option -l: length out of range (must be >=1)\n"); nathan@0: cd_len = (long long)cd_len * (1024*1024) / CD_FRAMESIZE; /* convert to blocks */ nathan@0: break; nathan@0: default: usage(); exit(0); nathan@0: } nathan@0: } nathan@0: nathan@0: if(!cd_mode && cd_track<0) /* need track number */ nathan@0: serror("A track number is required.\n"); nathan@0: } nathan@0: nathan@0: /****************************************************************************/ nathan@0: nathan@0: void print_track(int track, char *stamp, char *id, int disk, int startsec, int endsec) nathan@0: { nathan@0: char timestr[32], size[32]; nathan@0: nathan@0: snprintf(timestr,sizeof(timestr)-1,"%02d/%02d/%04d %02d:%02d", nathan@0: (stamp[4]-'0')*10 + (stamp[5]-'0'), nathan@0: (stamp[6]-'0')*10 + (stamp[7]-'0'), nathan@0: (stamp[0]-'0')*1000 + (stamp[1]-'0')*100 + (stamp[2]-'0')*10 + (stamp[3]-'0'), nathan@0: (stamp[8]-'0')*10 + (stamp[9]-'0'), nathan@0: (stamp[10]-'0')*10 + (stamp[11]-'0')); nathan@0: nathan@0: if(startsec>=0) snprintf(size,sizeof(size)-1," %3ld MB:",(long)((long long)(endsec-startsec)*CD_FRAMESIZE/(1024*1024))); nathan@0: else size[0]=0; nathan@0: nathan@0: fprintf(stderr,"Track %02d:%s %s Part %d: %s\n", track, size, timestr, disk, id); nathan@0: } nathan@0: nathan@0: /****************************************************************************/ nathan@0: nathan@0: int restore(int disknum, int disktrack) nathan@0: { nathan@0: int infile; nathan@0: int result=0, i, bytes; nathan@0: long long totalRead; nathan@0: struct header_block header; nathan@0: char buffer[CD_FRAMESIZE]; nathan@0: struct data_block *db=(struct data_block *)&buffer[0]; nathan@0: nathan@0: if ((infile = open(cd_dev, O_RDONLY)) < 0) error("Error opening device"); nathan@0: nathan@0: /* seek to proper CD-R(W) track */ nathan@0: for(i=tracks;i>0;i--) if(toc[i].track_no==disktrack) break; nathan@0: if(!i) { fprintf(stderr, "%s: Can't find track %d\n", prg_name, disktrack); exit(1); } nathan@0: nathan@0: totalRead=(long long)toc[i].sec_start*CD_FRAMESIZE; nathan@0: if(lseek64(infile,totalRead,SEEK_SET) != totalRead) error("Error seeking to track"); nathan@0: nathan@0: /* read header block */ nathan@0: bytes=full_read(infile,buffer,CD_FRAMESIZE); nathan@0: if (bytes < 0) error("Error reading header block"); nathan@0: if (bytes != CD_FRAMESIZE) error("Unexpected EOF reading header block"); nathan@0: totalRead = bytes; nathan@0: nathan@0: memcpy(&header,buffer,sizeof(header)); nathan@0: nathan@0: if(!strncmp(SHORT_HDR,header.id_str,strlen(SHORT_HDR))) { nathan@0: fprintf(stderr,"%s: ", prg_name); nathan@0: print_track(disktrack, header.t_stamp, header.vol_id, header.disk_set, -1, -1); nathan@0: nathan@0: if(disknum==1) { nathan@0: if(header.disk_set!=1) { nathan@0: fprintf(stderr,"%s: This is disk %d of a multi-disk set. Restore can only be started with disk 1!\n",prg_name,header.disk_set); nathan@0: exit(1); nathan@0: } nathan@0: headersave=header; /* save header for use with disk 2-n */ nathan@0: } nathan@0: else { nathan@0: if(strcmp(header.t_stamp,headersave.t_stamp) || strcmp(header.vol_id,headersave.vol_id)) { nathan@0: fprintf(stderr,"%s: This disk doesn't belong to the current set!\n",prg_name); nathan@0: result=2; nathan@0: } nathan@0: else if(header.disk_set!=disknum) { nathan@0: fprintf(stderr,"%s: Wrong sequence. You need disk %d now!\n",prg_name,disknum); nathan@0: result=2; nathan@0: } nathan@0: else fprintf(stderr, "%s: Beginning restore (Disk %d)\n", prg_name,disknum); nathan@0: } nathan@0: } nathan@0: else { nathan@0: fprintf(stderr, "%s: Track %02d was not created with 'cdbackup'\n", prg_name,disktrack); nathan@0: if(disknum==1) exit(1); nathan@0: result=2; nathan@0: } nathan@0: nathan@0: while(!result) { nathan@0: int size; nathan@0: nathan@0: /* read data block */ nathan@0: bytes = full_read(infile, buffer, CD_FRAMESIZE); nathan@0: if (bytes < 0) error("Error reading data"); nathan@0: if (bytes != CD_FRAMESIZE) error("Unexpected EOF reading data"); nathan@0: totalRead += bytes; nathan@0: nathan@0: /* sanity check */ nathan@0: size=ntohs(db->datasize); nathan@0: if(size>DATASIZE) { nathan@0: fprintf(stderr,"%s: Warning! Bad datasize at %lld\n",prg_name,totalRead); nathan@0: size=DATASIZE; nathan@0: } nathan@0: nathan@0: /* write the data block */ nathan@0: bytes=write(1,&buffer[DBSIZE], size); nathan@0: if(bytes!=size) error("Error writing data"); nathan@0: nathan@0: if(db->status == 1) break; /* end of backup*/ nathan@0: if(db->status == 2) result=1; /* disk full */ nathan@0: } nathan@0: nathan@0: /* print status */ nathan@0: totalSize+=totalRead; nathan@0: if(result!=2) fprintf(stderr, "%s: Restore complete. %lld kB read (%lld kB from this disk)\n",prg_name, totalSize/1024, totalRead/1024); nathan@0: nathan@0: close(infile); nathan@0: return(result); nathan@0: } nathan@0: nathan@0: /****************************************************************************/ nathan@0: nathan@0: void print_toc() nathan@0: { nathan@0: int i; nathan@0: nathan@0: fprintf(stderr,"Tracks: %d\n",tracks); nathan@0: print_space(); nathan@0: fprintf(stderr,"\n"); nathan@0: nathan@0: for (i = 1; i <= tracks; i++) { nathan@0: if(toc[i].is_data==0) fprintf(stderr,"Track %02d: Non-data\n", toc[i].track_no); nathan@0: else if (toc[i].is_cdbackup == 1) nathan@0: print_track(i, toc[i].t_stamp, toc[i].vol_id, toc[i].disk_set, toc[i].sec_start, i==tracks?toc[0].sec_start:toc[i+1].sec_start); nathan@0: else fprintf(stderr,"Track %02d: Data\n", toc[i].track_no); nathan@0: } nathan@0: } nathan@0: nathan@0: /****************************************************************************/ nathan@0: nathan@0: int main(int argc, char *argv[]) nathan@0: { nathan@0: int cdr; nathan@0: nathan@0: parse_cmdline(argc, argv); nathan@0: nathan@0: if(!cd_mode) { nathan@0: int disknum=1, result; nathan@0: totalSize=0; nathan@0: do { nathan@0: cdr=open_cdr(cd_dev); tracks=read_toc(cdr,0); close_cdr(cdr); nathan@0: nathan@0: result=restore(disknum,cd_track); nathan@0: if(result) { nathan@0: if(result==1) { disknum++; cd_track=1; } nathan@0: fprintf(stderr,"%s: Next disk needed: disk %d from %s\n",prg_name,disknum,headersave.vol_id); nathan@0: if(start_diskchange(multicmd,cd_dev)) serror("Diskchange command failed"); nathan@0: } nathan@0: } while(result); nathan@0: } nathan@0: else { nathan@0: cdr=open_cdr(cd_dev); tracks=read_toc(cdr,1); close_cdr(cdr); nathan@0: print_toc(); nathan@0: } nathan@0: nathan@0: return 0; nathan@0: }