2 Copyright (c) 2000-2006 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) {
88 serror("No memory for cdrecord args\n");
95 void start_cdrecord(void)
97 char **args, **p, *exname;
100 if(!(p=args=calloc(cdrec_opt_count+32,sizeof(char *))))
101 serror("No memory for cdrecord args\n");
103 if(dvd) exname="dvdrecord"; else exname="cdrecord";
106 if(virt_dump || dvd) {
108 *p++=make_arg("tsize=%ds",secs);
113 *p++=make_arg("padsize=%ds",padsize);
116 *p++=make_arg("speed=%d",cd_speed);
117 *p++=make_arg("dev=%s",cdr_dev);
119 for(l=0 ; l<cdrec_opt_count ; l++) *p++=cdrec_opt[l];
121 if(xamode2 && !dvd) *p++="-xa2"; else *p++="-data";
126 fprintf(stderr,"%s: cdrecord command:",prg_name);
127 for(p=args ; *p ; p++) fprintf(stderr," %s",*p);
128 fprintf(stderr,"\n");
132 error("Exec failed (cdrecord)");
135 long atip_cdrecord(void)
141 asprintf(&cmd,"%srecord 2>&1 dev=%s -atip",dvd ? "dvd":"cd",cdr_dev);
142 DEBUG("%s: cdrecord atip command: %s\n",prg_name,cmd);
145 if(!p) fprintf(stderr,"%s: atip command failed\n",prg_name);
148 while(fgets(buff,sizeof(buff),p)) {
151 if(!strncmp(buff,"rzone size:",11)) size=strtol(&buff[11],NULL,10);
153 else if(!strncmp(buff,"phys size:...",13)) size=strtol(&buff[13],NULL,10);
155 else if(!strncmp(buff," ATIP start of lead out:",25)) size=strtol(&buff[25],NULL,10);
160 if(size>0 && verbose) {
162 fprintf(stderr,"%s: auto-detected media size %s (%ld blocks)\n",prg_name,FlexSize(buff,(long long)size*CD_FRAMESIZE),size);
167 /****************************************************************************/
169 void parse_cmdline(char argc, char *argv[])
174 /* get some default from the environment */
175 val=getenv("CDR_DEVICE");
178 DEBUG("cdbackup: using recording device %s from CDR_DEVICE\n",cdr_dev);
180 val=getenv("CDR_SPEED");
182 cd_speed=strtol(val,NULL,10);
183 DEBUG("cdbackup: using speed %d from CDR_SPEED\n",cd_speed);
186 while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXDCi:wR"))>0) {
188 case 'V': fprintf(stderr,"cdbackup "VERSION" (compiled "__DATE__")\n"
189 "Copyright (C) 2000-2004\n"
190 "This is free software; see the source for copying conditions.\n"
191 "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
192 "PARTICULAR PURPOSE.\n");
194 case 'v': verbose=1; break;
195 case 'm': multidisk=1; break;
196 case 'X': xamode2=1; break;
197 case 'c': multicmd=optarg; break;
198 case 'd': cd_dev=optarg; break;
199 case 'r': cdr_dev=optarg; break;
200 case 'a': cd_label=optarg; break;
201 case 'C': crc=0; break;
202 case 'i': virt_name=optarg; virtual=1; break;
203 case 'w': virt_dump=1; break;
205 DEBUG("cdbackup: DVD mode enabled\n");
207 case 'D': verbose=1; debug=1;
208 DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n");
210 case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
212 case 's': errno=0; cd_speed=strtol(optarg,NULL,10);
213 if(errno==ERANGE || cd_speed<1) serror("Option -s: speed out of range (must be >=1)\n");
215 case 'p': errno=0; padsize=strtol(optarg,NULL,10);
216 if(errno==ERANGE || padsize<15) serror("Option -p: padsize out of range (must be >=15)\n");
218 default: fprintf(stderr,
219 "Usage: %s [options ...] [-- cdrecord-options ...]\n"
220 "Reads from standard input, block formats and writes to CD-R(W).\n\n"
221 " -d DEVICE DEVICE for CD queries (default /dev/burner)\n"
222 " -l N set media size, disable auto-detect\n"
223 " -r DEVICE DEVICE for CD recording (e.g. 0,4,0)\n"
224 " -s N record CD at speed N (default 4)\n"
225 " -X enable CDROM XA2 mode in cdrecord\n"
226 " -a LABEL use LABEL as CD session title\n"
227 " -p N use a padsize of N sectors for the session (default 15)\n"
228 " -m enable multi-disk mode\n"
229 " -c COMMAND call COMMAND on disk change in multi-disk mode\n"
230 " -C disable checksum creation for datablocks\n"
231 " -i IMAGE use virtual image IMAGE for recording\n"
232 " -w dump virtual image to media\n"
233 " -R enables DVD mode\n"
235 " -D enable DEBUG output\n"
236 " -V prints version & exits\n"
237 " -- pass rest of commandline to cdrecord\n"
243 if(optind<argc) { /* save position/count of cdrecord options */
244 cdrec_opt_count=argc-optind;
245 cdrec_opt=&argv[optind];
250 if(virtual && !virt_dump) serror("Can't auto-detect media size in virtual mode. Use option -l to set media size\n");
252 if(virtual && dvd && !virt_dump) {
253 fprintf(stderr,"Option -R ignored in virtual mode\n");
257 if(xamode2) fprintf(stderr,"Option -X ignored in DVD mode\n");
260 if(virt_dump && !virtual) serror("To dump an image you must supply the image name with -i\n");
261 if(!cdr_dev && (!virtual || virt_dump)) serror("You must specify a device for cdrecord with -r\n");
264 /****************************************************************************/
269 cd_len=atip_cdrecord();
270 if(cd_len<0) serror("Media size detection failed. Use option -l to set media size\n");
274 /****************************************************************************/
279 char buffer[CD_FRAMESIZE];
280 long long grandTotal;
287 Vopen(1); n=VreadToc(0);
288 if(n<1) serror("It's not usefull to dump an empty image");
289 secs=Vsize(); cont=VhasCont();
290 if(cd_len<secs) serror("Image doesn't fit to this media");
291 Vseek(-1); VprepareDump();
294 Vopen(0); n=VreadToc(0); VprintSpace();
296 fprintf(stderr,"Can't dump to non-empty disk! Try another disk\n");
302 diskchange(multicmd,cd_dev);
307 fprintf(stderr,"%s: Dumping image (%d blocks) to %s\n",prg_name,secs,VdevName());
313 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
319 totalSize+=grandTotal;
321 char str1[16], str2[16];
322 fprintf(stderr,"%s: Dumping finished. %s written (%s on this disk)\n",
323 prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
327 if(cont) fprintf(stderr,"Multi-disk not enabled, ignoring continuation image(s)!\n");
332 diskchange(multicmd,cd_dev);
337 /****************************************************************************/
341 long long grandTotal=0;
342 struct header_block header;
343 int flags, datasize, result=0;
345 char buffer[CD_FRAMESIZE];
346 struct data_block *db=(struct data_block *)&buffer[0];
350 if(crc) { flags|=F_CRC; datasize-=4; }
352 sprintf(buffer,"%04d%02d%02d%02d%02d",curtime.tm_year+1900,
353 curtime.tm_mon+1,curtime.tm_mday,curtime.tm_hour,curtime.tm_min);
355 strncpy(header.id_str,HDR_STRING,32); header.id_str[32]=0;
356 strncpy(header.vol_id,cd_label,32); header.vol_id[32]=0;
357 strncpy(header.t_stamp,buffer,12); header.t_stamp[12]=0;
358 header.disk_set = disknum;
359 header.flags = flags;
362 fprintf(stderr,"%s: Recording to %s, multidisk %s, CRC %s, disk %d\n",
364 multidisk?"enabled":"disabled",
365 crc?"enabled":"disabled",
370 memset(buffer,0,CD_FRAMESIZE);
371 memcpy(buffer,&header,sizeof(struct header_block));
372 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
378 db->status=0; /* this isn't the last block (for now) */
379 bytes=full_read(0,&buffer[DBSIZE],datasize);
380 if(bytes!=datasize) db->status=1; /* EOF, this is the last block */
381 db->datasize=htons(bytes);
383 if(cd_avail<(CD_FRAMESIZE*2)) { /* less than 2 block free */
384 if(db->status==0) { /* if not last block, mark disk as full */
390 int l=crc32(buffer,bytes+DBSIZE);
391 *((unsigned long *)(&buffer[CD_FRAMESIZE-4]))=l;
393 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
394 } while(db->status==0);
396 if(dvd && cd_avail>=CD_FRAMESIZE) { /* pad up the track with zeros */
397 memset(buffer,0,CD_FRAMESIZE);
398 if(verbose) fprintf(stderr,"%s: padding up the track\n",prg_name);
399 while(cd_avail>=CD_FRAMESIZE) Vwrite(buffer);
404 totalSize+=grandTotal;
406 char str1[16], str2[16];
407 fprintf(stderr,"%s: Recording finished. %s written (%s on this disk)\n",
408 prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
413 /****************************************************************************/
415 int main(int argc, char *argv[])
420 curtime_t=time(0); curtime=*localtime(&curtime_t);
421 parse_cmdline(argc,argv);
430 Vopen(0); result=VreadToc(0); VprintSpace();
433 if(disknum>1 && result!=0) {
435 fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name);
436 diskchange(multicmd,cd_dev);
438 else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
441 fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name);
442 diskchange(multicmd,cd_dev);
444 else serror("Not enough free space on disk");
451 if(multidisk==0) serror("Disk full, multi-disk not enabled. Aborting");
454 if(!VisRegular()) diskchange(multicmd,cd_dev);