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)) {
145 if(dvd && !strncmp(buff,"rzone size:",11)) size=strtol(&buff[12],NULL,10);
146 else if(!strncmp(buff," ATIP start of lead out:",25)) size=strtol(&buff[26],NULL,10);
151 if(size>0 && verbose) {
153 fprintf(stderr,"%s: auto-detected media size %s (%ld blocks)\n",prg_name,FlexSize(buff,(long long)size*CD_FRAMESIZE),size);
158 /****************************************************************************/
160 void parse_cmdline(char argc, char *argv[])
165 /* get some default from the environment */
166 val=getenv("CDR_DEVICE");
169 DEBUG("cdbackup: using recording device %s from CDR_DEVICE\n",cdr_dev);
171 val=getenv("CDR_SPEED");
173 cd_speed=strtol(val,NULL,10);
174 DEBUG("cdbackup: using speed %d from CDR_SPEED\n",cd_speed);
177 while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXDCi:wR"))>0) {
179 case 'V': fprintf(stderr,"cdbackup "VERSION" (compiled "__DATE__")\n"
180 "Copyright (C) 2000-2004\n"
181 "This is free software; see the source for copying conditions.\n"
182 "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
183 "PARTICULAR PURPOSE.\n");
185 case 'v': verbose=1; break;
186 case 'm': multidisk=1; break;
187 case 'X': xamode2=1; break;
188 case 'c': multicmd=optarg; break;
189 case 'd': cd_dev=optarg; break;
190 case 'r': cdr_dev=optarg; break;
191 case 'a': cd_label=optarg; break;
192 case 'C': crc=0; break;
193 case 'i': virt_name=optarg; virtual=1; break;
194 case 'w': virt_dump=1; break;
196 DEBUG("cdbackup: DVD mode enabled\n");
198 case 'D': verbose=1; debug=1;
199 DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n");
201 case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
203 case 's': errno=0; cd_speed=strtol(optarg,NULL,10);
204 if(errno==ERANGE || cd_speed<1) serror("Option -s: speed out of range (must be >=1)\n");
206 case 'p': errno=0; padsize=strtol(optarg,NULL,10);
207 if(errno==ERANGE || padsize<15) serror("Option -p: padsize out of range (must be >=15)\n");
209 default: fprintf(stderr,
210 "Usage: %s [options ...] [-- cdrecord-options ...]\n"
211 "Reads from standard input, block formats and writes to CD-R(W).\n\n"
212 " -d DEVICE DEVICE for CD queries (default /dev/burner)\n"
213 " -l N set media size, disable auto-detect\n"
214 " -r DEVICE DEVICE for CD recording (e.g. 0,4,0)\n"
215 " -s N record CD at speed N (default 4)\n"
216 " -X enable CDROM XA2 mode in cdrecord\n"
217 " -a LABEL use LABEL as CD session title\n"
218 " -p N use a padsize of N sectors for the session (default 15)\n"
219 " -m enable multi-disk mode\n"
220 " -c COMMAND call COMMAND on disk change in multi-disk mode\n"
221 " -C disable checksum creation for datablocks\n"
222 " -i IMAGE use virtual image IMAGE for recording\n"
223 " -w dump virtual image to media\n"
224 " -R enables DVD mode\n"
226 " -D enable DEBUG output\n"
227 " -V prints version & exits\n"
228 " -- pass rest of commandline to cdrecord\n"
234 if(optind<argc) { /* save position/count of cdrecord options */
235 cdrec_opt_count=argc-optind;
236 cdrec_opt=&argv[optind];
241 if(virtual && !virt_dump) serror("Can't auto-detect media size in virtual mode. Use option -l to set media size\n");
243 if(virtual && dvd && !virt_dump) {
244 fprintf(stderr,"Option -R ignored in virtual mode\n");
248 if(xamode2) fprintf(stderr,"Option -X ignored in DVD mode\n");
251 if(virt_dump && !virtual) serror("To dump an image you must supply the image name with -i\n");
252 if(!cdr_dev && (!virtual || virt_dump)) serror("You must specify a device for cdrecord with -r\n");
255 /****************************************************************************/
260 cd_len=atip_cdrecord();
261 if(cd_len<0) serror("Media size detection failed. Use option -l to set media size\n");
265 /****************************************************************************/
270 char buffer[CD_FRAMESIZE];
271 long long grandTotal;
278 Vopen(1); n=VreadToc(0);
279 if(n<1) serror("It's not usefull to dump an empty image");
280 secs=Vsize(); cont=VhasCont();
281 if(cd_avail<secs*CD_FRAMESIZE) serror("Image doesn't fits to media");
282 Vseek(-1); VprepareDump();
285 Vopen(0); n=VreadToc(0); VprintSpace();
287 fprintf(stderr,"Can't dump to non-empty disk! Try another disk\n");
293 diskchange(multicmd,cd_dev);
298 fprintf(stderr,"%s: Dumping image (%d blocks) to %s\n",prg_name,secs,VdevName());
304 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
310 totalSize+=grandTotal;
312 char str1[16], str2[16];
313 fprintf(stderr,"%s: Dumping finished. %s written (%s on this disk)\n",
314 prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
318 if(cont) fprintf(stderr,"Multi-disk not enabled, ignoring continuation image(s)!\n");
323 diskchange(multicmd,cd_dev);
328 /****************************************************************************/
332 long long grandTotal=0;
333 struct header_block header;
334 int flags, datasize, result=0;
336 char buffer[CD_FRAMESIZE];
337 struct data_block *db=(struct data_block *)&buffer[0];
341 if(crc) { flags|=F_CRC; datasize-=4; }
343 sprintf(buffer,"%04d%02d%02d%02d%02d",curtime.tm_year+1900,
344 curtime.tm_mon+1,curtime.tm_mday,curtime.tm_hour,curtime.tm_min);
346 strncpy(header.id_str,HDR_STRING,32); header.id_str[32]=0;
347 strncpy(header.vol_id,cd_label,32); header.vol_id[32]=0;
348 strncpy(header.t_stamp,buffer,12); header.t_stamp[12]=0;
349 header.disk_set = disknum;
350 header.flags = flags;
353 fprintf(stderr,"%s: Recording to %s, multidisk %s, CRC %s, disk %d\n",
355 multidisk?"enabled":"disabled",
356 crc?"enabled":"disabled",
361 memset(buffer,0,CD_FRAMESIZE);
362 memcpy(buffer,&header,sizeof(struct header_block));
363 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
369 db->status=0; /* this isn't the last block (for now) */
370 bytes=full_read(0,&buffer[DBSIZE],datasize);
371 if(bytes!=datasize) db->status=1; /* EOF, this is the last block */
372 db->datasize=htons(bytes);
374 if(cd_avail<(CD_FRAMESIZE*2)) { /* less than 2 block free */
375 if(db->status==0) { /* if not last block, mark disk as full */
381 int l=crc32(buffer,bytes+DBSIZE);
382 *((unsigned long *)(&buffer[CD_FRAMESIZE-4]))=l;
384 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
385 } while(db->status==0);
387 if(dvd && cd_avail>=CD_FRAMESIZE) { /* pad up the track with zeros */
388 memset(buffer,0,CD_FRAMESIZE);
389 if(verbose) fprintf(stderr,"%s: padding up the track\n",prg_name);
390 while(cd_avail>=CD_FRAMESIZE) Vwrite(buffer);
395 totalSize+=grandTotal;
397 char str1[16], str2[16];
398 fprintf(stderr,"%s: Recording finished. %s written (%s on this disk)\n",
399 prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
404 /****************************************************************************/
406 int main(int argc, char *argv[])
411 curtime_t=time(0); curtime=*localtime(&curtime_t);
412 parse_cmdline(argc,argv);
421 Vopen(0); result=VreadToc(0); VprintSpace();
424 if(disknum>1 && result!=0) {
426 fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name);
427 diskchange(multicmd,cd_dev);
429 else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
432 fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name);
433 diskchange(multicmd,cd_dev);
435 else serror("Not enough free space on disk");
442 if(multidisk==0) serror("Disk full, multi-disk not enabled. Aborting");
445 if(!VisRegular()) diskchange(multicmd,cd_dev);