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) { |
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 } |