2 Copyright (c) 2000-2004 Craig Condit, Stefan Hülswitt.
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 <sys/ioctl.h>
39 #include <netinet/in.h>
53 char *prg_name ="cdbackup";
54 char *cd_dev ="/dev/burner";
55 char *cdr_dev =0; /* no default here, too dangerous */
56 char *cd_label ="CDBackup Track";
58 long cd_len =-1; /* blocks */
59 int padsize =15; /* blocks */
72 int cdrec_opt_count=0;
74 long long totalSize=0;
77 struct tm curtime; /* global, so multi-disks get all the same time */
80 /****************************************************************************/
82 char *make_arg(const char *format, ...)
87 if(vasprintf(&ptr,format,ap)<0) serror("No memory for cdrecord args\n");
92 void start_cdrecord(void)
94 char **args, **p, *exname;
97 if(!(p=args=calloc(cdrec_opt_count+10,sizeof(char *))))
98 serror("No memory for cdrecord args\n");
100 if(dvd) exname="dvdrecord"; else exname="cdrecord";
103 if(virt_dump || dvd) {
105 *p++=make_arg("tsize=%ds",secs);
109 *p++=make_arg("padsize=%ds",padsize);
112 *p++=make_arg("speed=%d",cd_speed);
113 *p++=make_arg("dev=%s",cdr_dev);
115 for(l=0 ; l<cdrec_opt_count ; l++) *p++=cdrec_opt[l];
117 if(xamode2 && !dvd) *p++="-xa2"; else *p++="-data";
122 fprintf(stderr,"%s: cdrecord command:",prg_name);
123 for(p=args ; *p ; p++) fprintf(stderr," %s",*p);
124 fprintf(stderr,"\n");
128 error("Exec failed (cdrecord)");
131 long atip_cdrecord(void)
137 asprintf(&cmd,"%srecord 2>&1 dev=%s -atip",dvd ? "dvd":"cd",cdr_dev);
138 DEBUG("%s: cdrecord atip command: %s\n",prg_name,cmd);
141 if(!p) fprintf(stderr,"%s: atip command failed\n",prg_name);
144 while(fgets(buff,sizeof(buff),p)) {
147 if(!strncmp(buff,"rzone size:",11)) size=strtol(&buff[11],NULL,10);
149 else if(!strncmp(buff,"phys size:...",13)) size=strtol(&buff[13],NULL,10);
151 else if(!strncmp(buff," ATIP start of lead out:",25)) size=strtol(&buff[25],NULL,10);
156 if(size>0 && verbose) {
158 fprintf(stderr,"%s: auto-detected media size %s (%ld blocks)\n",prg_name,FlexSize(buff,(long long)size*CD_FRAMESIZE),size);
163 /****************************************************************************/
165 void parse_cmdline(char argc, char *argv[])
170 /* get some default from the environment */
171 val=getenv("CDR_DEVICE");
174 DEBUG("cdbackup: using recording device %s from CDR_DEVICE\n",cdr_dev);
176 val=getenv("CDR_SPEED");
178 cd_speed=strtol(val,NULL,10);
179 DEBUG("cdbackup: using speed %d from CDR_SPEED\n",cd_speed);
182 while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXDCi:wR"))>0) {
184 case 'V': fprintf(stderr,"cdbackup "VERSION" (compiled "__DATE__")\n"
185 "Copyright (C) 2000-2004\n"
186 "This is free software; see the source for copying conditions.\n"
187 "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
188 "PARTICULAR PURPOSE.\n");
190 case 'v': verbose=1; break;
191 case 'm': multidisk=1; break;
192 case 'X': xamode2=1; break;
193 case 'c': multicmd=optarg; break;
194 case 'd': cd_dev=optarg; break;
195 case 'r': cdr_dev=optarg; break;
196 case 'a': cd_label=optarg; break;
197 case 'C': crc=0; break;
198 case 'i': virt_name=optarg; virtual=1; break;
199 case 'w': virt_dump=1; break;
201 DEBUG("cdbackup: DVD mode enabled\n");
203 case 'D': verbose=1; debug=1;
204 DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n");
206 case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
208 case 's': errno=0; cd_speed=strtol(optarg,NULL,10);
209 if(errno==ERANGE || cd_speed<1) serror("Option -s: speed out of range (must be >=1)\n");
211 case 'p': errno=0; padsize=strtol(optarg,NULL,10);
212 if(errno==ERANGE || padsize<15) serror("Option -p: padsize out of range (must be >=15)\n");
214 default: fprintf(stderr,
215 "Usage: %s [options ...] [-- cdrecord-options ...]\n"
216 "Reads from standard input, block formats and writes to CD-R(W).\n\n"
217 " -d DEVICE DEVICE for CD queries (default /dev/burner)\n"
218 " -l N set media size, disable auto-detect\n"
219 " -r DEVICE DEVICE for CD recording (e.g. 0,4,0)\n"
220 " -s N record CD at speed N (default 4)\n"
221 " -X enable CDROM XA2 mode in cdrecord\n"
222 " -a LABEL use LABEL as CD session title\n"
223 " -p N use a padsize of N sectors for the session (default 15)\n"
224 " -m enable multi-disk mode\n"
225 " -c COMMAND call COMMAND on disk change in multi-disk mode\n"
226 " -C disable checksum creation for datablocks\n"
227 " -i IMAGE use virtual image IMAGE for recording\n"
228 " -w dump virtual image to media\n"
229 " -R enables DVD mode\n"
231 " -D enable DEBUG output\n"
232 " -V prints version & exits\n"
233 " -- pass rest of commandline to cdrecord\n"
239 if(optind<argc) { /* save position/count of cdrecord options */
240 cdrec_opt_count=argc-optind;
241 cdrec_opt=&argv[optind];
246 if(virtual && !virt_dump) serror("Can't auto-detect media size in virtual mode. Use option -l to set media size\n");
248 if(virtual && dvd && !virt_dump) {
249 fprintf(stderr,"Option -R ignored in virtual mode\n");
253 if(xamode2) fprintf(stderr,"Option -X ignored in DVD mode\n");
256 if(virt_dump && !virtual) serror("To dump an image you must supply the image name with -i\n");
257 if(!cdr_dev && (!virtual || virt_dump)) serror("You must specify a device for cdrecord with -r\n");
260 /****************************************************************************/
265 cd_len=atip_cdrecord();
266 if(cd_len<0) serror("Media size detection failed. Use option -l to set media size\n");
270 /****************************************************************************/
275 char buffer[CD_FRAMESIZE];
276 long long grandTotal;
283 Vopen(1); n=VreadToc(0);
284 if(n<1) serror("It's not usefull to dump an empty image");
285 secs=Vsize(); cont=VhasCont();
286 if(cd_len<secs) serror("Image doesn't fit to this media");
287 Vseek(-1); VprepareDump();
290 Vopen(0); n=VreadToc(0); VprintSpace();
292 fprintf(stderr,"Can't dump to non-empty disk! Try another disk\n");
298 diskchange(multicmd,cd_dev);
303 fprintf(stderr,"%s: Dumping image (%d blocks) to %s\n",prg_name,secs,VdevName());
309 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
315 totalSize+=grandTotal;
317 char str1[16], str2[16];
318 fprintf(stderr,"%s: Dumping finished. %s written (%s on this disk)\n",
319 prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
323 if(cont) fprintf(stderr,"Multi-disk not enabled, ignoring continuation image(s)!\n");
328 diskchange(multicmd,cd_dev);
333 /****************************************************************************/
337 long long grandTotal=0;
338 struct header_block header;
339 int flags, datasize, result=0;
341 char buffer[CD_FRAMESIZE];
342 struct data_block *db=(struct data_block *)&buffer[0];
346 if(crc) { flags|=F_CRC; datasize-=4; }
348 sprintf(buffer,"%04d%02d%02d%02d%02d",curtime.tm_year+1900,
349 curtime.tm_mon+1,curtime.tm_mday,curtime.tm_hour,curtime.tm_min);
351 strncpy(header.id_str,HDR_STRING,32); header.id_str[32]=0;
352 strncpy(header.vol_id,cd_label,32); header.vol_id[32]=0;
353 strncpy(header.t_stamp,buffer,12); header.t_stamp[12]=0;
354 header.disk_set = disknum;
355 header.flags = flags;
358 fprintf(stderr,"%s: Recording to %s, multidisk %s, CRC %s, disk %d\n",
360 multidisk?"enabled":"disabled",
361 crc?"enabled":"disabled",
366 memset(buffer,0,CD_FRAMESIZE);
367 memcpy(buffer,&header,sizeof(struct header_block));
368 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
374 db->status=0; /* this isn't the last block (for now) */
375 bytes=full_read(0,&buffer[DBSIZE],datasize);
376 if(bytes!=datasize) db->status=1; /* EOF, this is the last block */
377 db->datasize=htons(bytes);
379 if(cd_avail<(CD_FRAMESIZE*2)) { /* less than 2 block free */
380 if(db->status==0) { /* if not last block, mark disk as full */
386 int l=crc32(buffer,bytes+DBSIZE);
387 *((unsigned long *)(&buffer[CD_FRAMESIZE-4]))=l;
389 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
390 } while(db->status==0);
392 if(dvd && cd_avail>=CD_FRAMESIZE) { /* pad up the track with zeros */
393 memset(buffer,0,CD_FRAMESIZE);
394 if(verbose) fprintf(stderr,"%s: padding up the track\n",prg_name);
395 while(cd_avail>=CD_FRAMESIZE) Vwrite(buffer);
400 totalSize+=grandTotal;
402 char str1[16], str2[16];
403 fprintf(stderr,"%s: Recording finished. %s written (%s on this disk)\n",
404 prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
409 /****************************************************************************/
411 int main(int argc, char *argv[])
416 curtime_t=time(0); curtime=*localtime(&curtime_t);
417 parse_cmdline(argc,argv);
426 Vopen(0); result=VreadToc(0); VprintSpace();
429 if(disknum>1 && result!=0) {
431 fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name);
432 diskchange(multicmd,cd_dev);
434 else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
437 fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name);
438 diskchange(multicmd,cd_dev);
440 else serror("Not enough free space on disk");
447 if(multidisk==0) serror("Disk full, multi-disk not enabled. Aborting");
450 if(!VisRegular()) diskchange(multicmd,cd_dev);