2 Copyright (c) 2000-2012 Craig Condit, Stefan Huelswitt.
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
7 1. Redistributions of source code must retain the above copyright notice,
8 this list of conditions and the following disclaimer.
9 2. Redistributions in binary form must reproduce the above copyright notice,
10 this list of conditions and the following disclaimer in the documentation
11 and/or other materials provided with the distribution.
13 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
14 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
17 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 #define _LARGEFILE64_SOURCE
38 #include <netinet/in.h>
48 char *prg_name ="cdrestore";
50 char *cd_dev ="/dev/cdrom";
51 long cd_len =333000; /* blocks */
65 struct header_block headersave;
67 /****************************************************************************/
69 void parse_cmdline(char argc, char *argv[])
73 while ((i=getopt(argc,argv,"d:l:c:t:qvVFTDRi:"))>0) {
75 case 'V': fprintf(stderr,"cdrestore "VERSION" (compiled "__DATE__")\n"
76 "Copyright (C) 2000-2004\n"
77 "This is free software; see the source for copying conditions.\n"
78 "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
79 "PARTICULAR PURPOSE.\n");
81 case 'c': multicmd=optarg; break;
82 case 'd': cd_dev=optarg; break;
83 case 'q': query=1; break;
84 case 'F': force=1; break;
85 case 'R': ahead=1; break;
86 case 'T': verify=1; break;
87 case 'v': verbose=1; break;
88 case 'i': virt_name=optarg; virtual=1; break;
89 case 'D': verbose=1; debug=1;
90 DEBUG("cdrestore: DEBUG output enabled ("VERSION")\n");
92 case 't': errno=0; cd_track=strtol(optarg,NULL,10);
93 if(errno==ERANGE || cd_track<1) serror("Option -t: invalid track (must be >=1)\n");
95 case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
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"
110 " -D enable DEBUG output\n"
111 " -V prints version & exits\n"
117 if(!query && cd_track<0) /* need track number */
118 serror("A track number is required.\n");
121 /****************************************************************************/
123 void print_track(int track, char *stamp, char *id, int disk, int startsec, int endsec, char flags)
125 char timestr[32], size[32], flstr[12];
127 snprintf(timestr,sizeof(timestr),"%02d/%02d/%04d %02d:%02d",
128 (stamp[4]-'0')*10 + (stamp[5]-'0'),
129 (stamp[6]-'0')*10 + (stamp[7]-'0'),
130 (stamp[0]-'0')*1000 + (stamp[1]-'0')*100 + (stamp[2]-'0')*10 + (stamp[3]-'0'),
131 (stamp[8]-'0')*10 + (stamp[9]-'0'),
132 (stamp[10]-'0')*10 + (stamp[11]-'0'));
134 if(startsec>=0) snprintf(size,sizeof(size)," %s:",FlexSize(flstr,((long long)(endsec-startsec+1)*CD_FRAMESIZE)));
136 snprintf(flstr,sizeof(flstr),"%c",flags&F_CRC?'C':'.');
138 fprintf(stderr,"Track %02d:%s %s Part %d %s : %s\n", track, size, timestr, disk, flstr, id);
139 if(startsec>=0) DEBUG(" Start sector %7d Last sector %7d\n",startsec,endsec);
142 /****************************************************************************/
144 int restore(int disktrack)
146 int result=0, i, bytes;
147 long long totalRead=0, startPos;
148 struct header_block header;
149 char buffer[CD_FRAMESIZE];
150 struct data_block *db=(struct data_block *)&buffer[0];
152 for(i=tracks-1; i>=0; i--) if(toc[i].track_no==disktrack) break;
153 if(i<0) { fprintf(stderr, "%s: Can't find track %d\n", prg_name, disktrack); exit(1); }
156 Vread(buffer); totalRead+=CD_FRAMESIZE;
157 memcpy(&header,buffer,sizeof(header));
158 if(!strncmp(SHORT_HDR,header.id_str,strlen(SHORT_HDR))) {
160 fprintf(stderr,"%s: ", prg_name);
161 print_track(disktrack, header.t_stamp, header.vol_id, header.disk_set, -1, -1, header.flags);
165 if(header.disk_set!=1) {
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);
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);
171 disknum=header.disk_set;
173 headersave=header; /* save header for use with disk 2-n */
176 if(strcmp(header.t_stamp,headersave.t_stamp) || strcmp(header.vol_id,headersave.vol_id)) {
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);
180 else if(header.disk_set!=disknum) {
181 fprintf(stderr,"%s: Wrong sequence. This is disk %d, but you need disk %d now!\n",prg_name,header.disk_set,disknum);
184 else if(verbose) fprintf(stderr, "%s: Beginning restore (Disk %d)\n", prg_name,disknum);
188 fprintf(stderr, "%s: Track %02d was not created with 'cdbackup'\n", prg_name,disktrack);
189 if(disknum==1) exit(1);
196 DEBUG("\rReading sector %7ld ",(long)((startPos+totalRead)/CD_FRAMESIZE));
199 size=ntohs(db->datasize);
201 if(verbose) fprintf(stderr,"%s: Warning! Bad datasize at %lld\n",prg_name,totalRead);
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");
213 totalRead+=CD_FRAMESIZE;
216 bytes=write(1,&buffer[DBSIZE],size);
217 if(bytes!=size) error("Write failed (stdout)");
220 if(db->status == 1) break; /* end of backup*/
221 if(db->status == 2) result=1; /* next disk */
226 totalSize+=totalRead;
227 if(result>=0 && verbose) {
228 char str1[16], str2[16];
229 fprintf(stderr, "%s: Restore complete. %s read (%s from this disk)\n",
230 prg_name,FlexSize(str1,totalSize),FlexSize(str2,totalRead));
236 /****************************************************************************/
242 fprintf(stderr,"Tracks: %d\n",tracks);
244 fprintf(stderr,"\n");
246 for(i=0; i<tracks; i++) {
248 fprintf(stderr,"Track %02d: Non-data\n",toc[i].track_no);
249 else if(toc[i].is_cdbackup)
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);
252 fprintf(stderr,"Track %02d: Data\n", toc[i].track_no);
256 /****************************************************************************/
258 int main(int argc, char *argv[])
260 parse_cmdline(argc, argv);
262 disknum=1; totalSize=0;
263 Vopen(1); tracks=VreadToc(query || debug);
270 if(verify) fprintf(stderr,"%s: Verify mode enabled, no data output!\n",prg_name);
271 if(ahead) { VgetAhead(); VsetAhead(0); }
273 result=restore(cd_track);
275 if(result>0) { disknum++; cd_track=1; }
278 fprintf(stderr,"%s: Next disk needed: disk %d from %s\n",prg_name,disknum,headersave.vol_id);
279 diskchange(multicmd,cd_dev);
281 else if(result<0) break;
282 Vopen(1); tracks=VreadToc(0);
285 if(ahead) VsetAhead(1);