|
1 /* cdrestore.c |
|
2 Copyright (c) 2000-2002 Craig Condit, Stefan Hülswitt. |
|
3 |
|
4 Redistribution and use in source and binary forms, with or without |
|
5 modification, are permitted provided that the following conditions are met: |
|
6 |
|
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. |
|
12 |
|
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 |
|
23 SUCH DAMAGE. |
|
24 */ |
|
25 |
|
26 #define _LARGEFILE64_SOURCE |
|
27 |
|
28 #include <stdio.h> |
|
29 #include <stdlib.h> |
|
30 #include <string.h> |
|
31 #include <unistd.h> |
|
32 #include <fcntl.h> |
|
33 #include <time.h> |
|
34 #include <errno.h> |
|
35 #include <getopt.h> |
|
36 #include <sys/ioctl.h> |
|
37 #include <sys/wait.h> |
|
38 #include <netinet/in.h> |
|
39 #include <linux/cdrom.h> |
|
40 |
|
41 #include "cdbackup.h" |
|
42 #include "cdrom.h" |
|
43 #include "misc.h" |
|
44 #include "version.h" |
|
45 |
|
46 /* defaults */ |
|
47 char * prg_name ="cdrestore"; |
|
48 int cd_mode =0; |
|
49 int cd_track =-1; |
|
50 char * cd_dev ="/dev/cdrom"; |
|
51 long cd_len =333000; /* blocks */ |
|
52 char * multicmd =0; |
|
53 int verbose =1; |
|
54 |
|
55 int tracks; |
|
56 long long totalSize; |
|
57 struct header_block headersave; |
|
58 |
|
59 /****************************************************************************/ |
|
60 |
|
61 void usage() |
|
62 { |
|
63 fprintf(stderr, |
|
64 "Usage: %s [OPTION]...\n" |
|
65 "Reads block input from CD-R(W) and writes it to standard output.\n\n" |
|
66 " -d DEVICE DEVICE for CD queries (e.g. /dev/sr0)\n" |
|
67 " -q query disk and print TOC only\n" |
|
68 " -t N restore from track N\n" |
|
69 " -l N CD-R has a size of N MB\n" |
|
70 " -c COMMAND call COMMAND on disk change in multi-disk mode\n" |
|
71 " -V prints version & exits\n" |
|
72 "\n", prg_name); |
|
73 } |
|
74 |
|
75 /****************************************************************************/ |
|
76 |
|
77 void parse_cmdline(char argc, char *argv[]) |
|
78 { |
|
79 int i; |
|
80 |
|
81 while ((i=getopt(argc,argv,"d:l:c:t:qV"))>0) { |
|
82 switch (i) { |
|
83 case 'V': fprintf(stderr,"cdrestore "VERSION" (compiled "__DATE__")\n" |
|
84 "Copyright (C) 2000-2002\n" |
|
85 "This is free software; see the source for copying conditions.\n" |
|
86 "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n" |
|
87 "PARTICULAR PURPOSE.\n"); |
|
88 exit(0); |
|
89 case 'c': multicmd=optarg; break; |
|
90 case 'd': cd_dev=optarg; break; |
|
91 case 'q': cd_mode=1; break; |
|
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"); |
|
94 break; |
|
95 case 'l': errno=0; cd_len=strtol(optarg,NULL,10); |
|
96 if(errno==ERANGE || cd_len<1) serror("Option -l: length out of range (must be >=1)\n"); |
|
97 cd_len = (long long)cd_len * (1024*1024) / CD_FRAMESIZE; /* convert to blocks */ |
|
98 break; |
|
99 default: usage(); exit(0); |
|
100 } |
|
101 } |
|
102 |
|
103 if(!cd_mode && cd_track<0) /* need track number */ |
|
104 serror("A track number is required.\n"); |
|
105 } |
|
106 |
|
107 /****************************************************************************/ |
|
108 |
|
109 void print_track(int track, char *stamp, char *id, int disk, int startsec, int endsec) |
|
110 { |
|
111 char timestr[32], size[32]; |
|
112 |
|
113 snprintf(timestr,sizeof(timestr)-1,"%02d/%02d/%04d %02d:%02d", |
|
114 (stamp[4]-'0')*10 + (stamp[5]-'0'), |
|
115 (stamp[6]-'0')*10 + (stamp[7]-'0'), |
|
116 (stamp[0]-'0')*1000 + (stamp[1]-'0')*100 + (stamp[2]-'0')*10 + (stamp[3]-'0'), |
|
117 (stamp[8]-'0')*10 + (stamp[9]-'0'), |
|
118 (stamp[10]-'0')*10 + (stamp[11]-'0')); |
|
119 |
|
120 if(startsec>=0) snprintf(size,sizeof(size)-1," %3ld MB:",(long)((long long)(endsec-startsec)*CD_FRAMESIZE/(1024*1024))); |
|
121 else size[0]=0; |
|
122 |
|
123 fprintf(stderr,"Track %02d:%s %s Part %d: %s\n", track, size, timestr, disk, id); |
|
124 } |
|
125 |
|
126 /****************************************************************************/ |
|
127 |
|
128 int restore(int disknum, int disktrack) |
|
129 { |
|
130 int infile; |
|
131 int result=0, i, bytes; |
|
132 long long totalRead; |
|
133 struct header_block header; |
|
134 char buffer[CD_FRAMESIZE]; |
|
135 struct data_block *db=(struct data_block *)&buffer[0]; |
|
136 |
|
137 if ((infile = open(cd_dev, O_RDONLY)) < 0) error("Error opening device"); |
|
138 |
|
139 /* seek to proper CD-R(W) track */ |
|
140 for(i=tracks;i>0;i--) if(toc[i].track_no==disktrack) break; |
|
141 if(!i) { fprintf(stderr, "%s: Can't find track %d\n", prg_name, disktrack); exit(1); } |
|
142 |
|
143 totalRead=(long long)toc[i].sec_start*CD_FRAMESIZE; |
|
144 if(lseek64(infile,totalRead,SEEK_SET) != totalRead) error("Error seeking to track"); |
|
145 |
|
146 /* read header block */ |
|
147 bytes=full_read(infile,buffer,CD_FRAMESIZE); |
|
148 if (bytes < 0) error("Error reading header block"); |
|
149 if (bytes != CD_FRAMESIZE) error("Unexpected EOF reading header block"); |
|
150 totalRead = bytes; |
|
151 |
|
152 memcpy(&header,buffer,sizeof(header)); |
|
153 |
|
154 if(!strncmp(SHORT_HDR,header.id_str,strlen(SHORT_HDR))) { |
|
155 fprintf(stderr,"%s: ", prg_name); |
|
156 print_track(disktrack, header.t_stamp, header.vol_id, header.disk_set, -1, -1); |
|
157 |
|
158 if(disknum==1) { |
|
159 if(header.disk_set!=1) { |
|
160 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); |
|
161 exit(1); |
|
162 } |
|
163 headersave=header; /* save header for use with disk 2-n */ |
|
164 } |
|
165 else { |
|
166 if(strcmp(header.t_stamp,headersave.t_stamp) || strcmp(header.vol_id,headersave.vol_id)) { |
|
167 fprintf(stderr,"%s: This disk doesn't belong to the current set!\n",prg_name); |
|
168 result=2; |
|
169 } |
|
170 else if(header.disk_set!=disknum) { |
|
171 fprintf(stderr,"%s: Wrong sequence. You need disk %d now!\n",prg_name,disknum); |
|
172 result=2; |
|
173 } |
|
174 else fprintf(stderr, "%s: Beginning restore (Disk %d)\n", prg_name,disknum); |
|
175 } |
|
176 } |
|
177 else { |
|
178 fprintf(stderr, "%s: Track %02d was not created with 'cdbackup'\n", prg_name,disktrack); |
|
179 if(disknum==1) exit(1); |
|
180 result=2; |
|
181 } |
|
182 |
|
183 while(!result) { |
|
184 int size; |
|
185 |
|
186 /* read data block */ |
|
187 bytes = full_read(infile, buffer, CD_FRAMESIZE); |
|
188 if (bytes < 0) error("Error reading data"); |
|
189 if (bytes != CD_FRAMESIZE) error("Unexpected EOF reading data"); |
|
190 totalRead += bytes; |
|
191 |
|
192 /* sanity check */ |
|
193 size=ntohs(db->datasize); |
|
194 if(size>DATASIZE) { |
|
195 fprintf(stderr,"%s: Warning! Bad datasize at %lld\n",prg_name,totalRead); |
|
196 size=DATASIZE; |
|
197 } |
|
198 |
|
199 /* write the data block */ |
|
200 bytes=write(1,&buffer[DBSIZE], size); |
|
201 if(bytes!=size) error("Error writing data"); |
|
202 |
|
203 if(db->status == 1) break; /* end of backup*/ |
|
204 if(db->status == 2) result=1; /* disk full */ |
|
205 } |
|
206 |
|
207 /* print status */ |
|
208 totalSize+=totalRead; |
|
209 if(result!=2) fprintf(stderr, "%s: Restore complete. %lld kB read (%lld kB from this disk)\n",prg_name, totalSize/1024, totalRead/1024); |
|
210 |
|
211 close(infile); |
|
212 return(result); |
|
213 } |
|
214 |
|
215 /****************************************************************************/ |
|
216 |
|
217 void print_toc() |
|
218 { |
|
219 int i; |
|
220 |
|
221 fprintf(stderr,"Tracks: %d\n",tracks); |
|
222 print_space(); |
|
223 fprintf(stderr,"\n"); |
|
224 |
|
225 for (i = 1; i <= tracks; i++) { |
|
226 if(toc[i].is_data==0) fprintf(stderr,"Track %02d: Non-data\n", toc[i].track_no); |
|
227 else if (toc[i].is_cdbackup == 1) |
|
228 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); |
|
229 else fprintf(stderr,"Track %02d: Data\n", toc[i].track_no); |
|
230 } |
|
231 } |
|
232 |
|
233 /****************************************************************************/ |
|
234 |
|
235 int main(int argc, char *argv[]) |
|
236 { |
|
237 int cdr; |
|
238 |
|
239 parse_cmdline(argc, argv); |
|
240 |
|
241 if(!cd_mode) { |
|
242 int disknum=1, result; |
|
243 totalSize=0; |
|
244 do { |
|
245 cdr=open_cdr(cd_dev); tracks=read_toc(cdr,0); close_cdr(cdr); |
|
246 |
|
247 result=restore(disknum,cd_track); |
|
248 if(result) { |
|
249 if(result==1) { disknum++; cd_track=1; } |
|
250 fprintf(stderr,"%s: Next disk needed: disk %d from %s\n",prg_name,disknum,headersave.vol_id); |
|
251 if(start_diskchange(multicmd,cd_dev)) serror("Diskchange command failed"); |
|
252 } |
|
253 } while(result); |
|
254 } |
|
255 else { |
|
256 cdr=open_cdr(cd_dev); tracks=read_toc(cdr,1); close_cdr(cdr); |
|
257 print_toc(); |
|
258 } |
|
259 |
|
260 return 0; |
|
261 } |