nathan@0: /* cdbackup.c nathan@12: Copyright (c) 2000-2010 Craig Condit, Stefan Hülswitt. nathan@0: nathan@0: Redistribution and use in source and binary forms, with or without nathan@0: modification, are permitted provided that the following conditions are met: nathan@0: nathan@0: 1. Redistributions of source code must retain the above copyright notice, nathan@0: this list of conditions and the following disclaimer. nathan@0: 2. Redistributions in binary form must reproduce the above copyright notice, nathan@0: this list of conditions and the following disclaimer in the documentation nathan@0: and/or other materials provided with the distribution. nathan@0: nathan@0: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS nathan@0: OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED nathan@0: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE nathan@0: DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR nathan@0: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL nathan@0: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR nathan@0: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER nathan@0: CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT nathan@0: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY nathan@0: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF nathan@0: SUCH DAMAGE. nathan@0: */ nathan@0: nathan@0: #define _LARGEFILE64_SOURCE nathan@4: #define _GNU_SOURCE nathan@0: nathan@0: #include nathan@0: #include nathan@4: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@4: nathan@4: #ifndef sun nathan@4: #include nathan@4: #endif nathan@0: nathan@0: #include "cdbackup.h" nathan@0: #include "cdrom.h" nathan@4: #include "virtual.h" nathan@0: #include "misc.h" nathan@2: #include "debug.h" nathan@0: #include "version.h" nathan@0: nathan@0: /* defaults */ nathan@4: char *prg_name ="cdbackup"; nathan@4: char *cd_dev ="/dev/burner"; nathan@4: char *cdr_dev =0; /* no default here, too dangerous */ nathan@4: char *cd_label ="CDBackup Track"; nathan@4: int cd_speed =4; nathan@4: long cd_len =-1; /* blocks */ nathan@4: int padsize =15; /* blocks */ nathan@4: int multidisk=0; nathan@4: char *multicmd =0; nathan@4: int verbose =0; nathan@4: int xamode2 =0; nathan@4: int crc =1; nathan@4: int debug =0; nathan@4: int virtual =0; nathan@4: char *virt_name=0; nathan@4: int virt_dump=0; nathan@4: int dvd =0; nathan@12: char *exename ="cdrecord"; nathan@0: nathan@0: char **cdrec_opt=0; nathan@0: int cdrec_opt_count=0; nathan@0: nathan@4: long long totalSize=0; nathan@4: int disknum=1; nathan@4: int secs; nathan@0: struct tm curtime; /* global, so multi-disks get all the same time */ nathan@4: int auto_size=0; nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: char *make_arg(const char *format, ...) nathan@4: { nathan@4: char *ptr; nathan@4: va_list ap; nathan@4: va_start(ap,format); nathan@10: if(vasprintf(&ptr,format,ap)<0) { nathan@10: serror("No memory for cdrecord args\n"); nathan@10: ptr=0; nathan@10: } nathan@4: va_end(ap); nathan@4: return ptr; nathan@4: } nathan@4: nathan@4: void start_cdrecord(void) nathan@4: { nathan@12: char **args, **p; nathan@4: int l; nathan@4: nathan@10: if(!(p=args=calloc(cdrec_opt_count+32,sizeof(char *)))) nathan@4: serror("No memory for cdrecord args\n"); nathan@4: nathan@12: *p++=exename; nathan@4: nathan@4: if(virt_dump || dvd) { nathan@4: *p++="-dao"; nathan@4: *p++=make_arg("tsize=%ds",secs); nathan@4: } nathan@4: else { nathan@4: *p++="-multi"; nathan@10: *p++="-tao"; nathan@4: *p++=make_arg("padsize=%ds",padsize); nathan@4: } nathan@4: nathan@4: *p++=make_arg("speed=%d",cd_speed); nathan@4: *p++=make_arg("dev=%s",cdr_dev); nathan@4: nathan@4: for(l=0 ; l&1 dev=%s -atip",exename,cdr_dev)<0) { nathan@13: fprintf(stderr,"%s: error making atip command: %s\n",prg_name,strerror(errno)); nathan@13: return -1; nathan@13: } nathan@4: DEBUG("%s: cdrecord atip command: %s\n",prg_name,cmd); nathan@4: nathan@4: p=popen(cmd,"r"); nathan@4: if(!p) fprintf(stderr,"%s: atip command failed\n",prg_name); nathan@4: else { nathan@4: char buff[256]; nathan@4: while(fgets(buff,sizeof(buff),p)) { nathan@6: if(dvd) { nathan@6: /* DVD-R */ nathan@6: if(!strncmp(buff,"rzone size:",11)) size=strtol(&buff[11],NULL,10); nathan@6: /* DVD+R */ nathan@6: else if(!strncmp(buff,"phys size:...",13)) size=strtol(&buff[13],NULL,10); nathan@6: } nathan@6: else if(!strncmp(buff," ATIP start of lead out:",25)) size=strtol(&buff[25],NULL,10); nathan@4: } nathan@4: } nathan@4: pclose(p); nathan@4: free(cmd); nathan@4: if(size>0 && verbose) { nathan@4: char buff[16]; nathan@4: fprintf(stderr,"%s: auto-detected media size %s (%ld blocks)\n",prg_name,FlexSize(buff,(long long)size*CD_FRAMESIZE),size); nathan@4: } nathan@4: return size; nathan@0: } nathan@0: nathan@0: /****************************************************************************/ nathan@0: nathan@0: void parse_cmdline(char argc, char *argv[]) nathan@0: { nathan@0: int i; nathan@4: char *val; nathan@4: nathan@4: /* get some default from the environment */ nathan@4: val=getenv("CDR_DEVICE"); nathan@4: if(val) { nathan@4: cdr_dev=strdup(val); nathan@4: DEBUG("cdbackup: using recording device %s from CDR_DEVICE\n",cdr_dev); nathan@4: } nathan@4: val=getenv("CDR_SPEED"); nathan@4: if(val) { nathan@4: cd_speed=strtol(val,NULL,10); nathan@4: DEBUG("cdbackup: using speed %d from CDR_SPEED\n",cd_speed); nathan@4: } nathan@4: nathan@12: while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXDCi:wRE:"))>0) { nathan@0: switch (i) { nathan@0: case 'V': fprintf(stderr,"cdbackup "VERSION" (compiled "__DATE__")\n" nathan@12: "Copyright (C) 2000-2010\n" nathan@0: "This is free software; see the source for copying conditions.\n" nathan@0: "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n" nathan@0: "PARTICULAR PURPOSE.\n"); nathan@0: exit(0); nathan@0: case 'v': verbose=1; break; nathan@0: case 'm': multidisk=1; break; nathan@0: case 'X': xamode2=1; break; nathan@0: case 'c': multicmd=optarg; break; nathan@0: case 'd': cd_dev=optarg; break; nathan@0: case 'r': cdr_dev=optarg; break; nathan@0: case 'a': cd_label=optarg; break; nathan@4: case 'C': crc=0; break; nathan@4: case 'i': virt_name=optarg; virtual=1; break; nathan@4: case 'w': virt_dump=1; break; nathan@12: case 'E': exename=optarg; break; nathan@4: case 'R': dvd=1; nathan@4: DEBUG("cdbackup: DVD mode enabled\n"); nathan@4: break; nathan@2: case 'D': verbose=1; debug=1; nathan@2: DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n"); nathan@2: break; nathan@4: case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE); nathan@4: break; nathan@0: case 's': errno=0; cd_speed=strtol(optarg,NULL,10); nathan@0: if(errno==ERANGE || cd_speed<1) serror("Option -s: speed out of range (must be >=1)\n"); nathan@0: break; nathan@0: case 'p': errno=0; padsize=strtol(optarg,NULL,10); nathan@0: if(errno==ERANGE || padsize<15) serror("Option -p: padsize out of range (must be >=15)\n"); nathan@0: break; nathan@4: default: fprintf(stderr, nathan@4: "Usage: %s [options ...] [-- cdrecord-options ...]\n" nathan@4: "Reads from standard input, block formats and writes to CD-R(W).\n\n" nathan@4: " -d DEVICE DEVICE for CD queries (default /dev/burner)\n" nathan@4: " -l N set media size, disable auto-detect\n" nathan@4: " -r DEVICE DEVICE for CD recording (e.g. 0,4,0)\n" nathan@4: " -s N record CD at speed N (default 4)\n" nathan@4: " -X enable CDROM XA2 mode in cdrecord\n" nathan@4: " -a LABEL use LABEL as CD session title\n" nathan@4: " -p N use a padsize of N sectors for the session (default 15)\n" nathan@4: " -m enable multi-disk mode\n" nathan@4: " -c COMMAND call COMMAND on disk change in multi-disk mode\n" nathan@4: " -C disable checksum creation for datablocks\n" nathan@4: " -i IMAGE use virtual image IMAGE for recording\n" nathan@4: " -w dump virtual image to media\n" nathan@4: " -R enables DVD mode\n" nathan@12: " -E EXE set alternative cdrecord executable\n" nathan@4: " -v be verbose\n" nathan@4: " -D enable DEBUG output\n" nathan@4: " -V prints version & exits\n" nathan@4: " -- pass rest of commandline to cdrecord\n" nathan@4: "\n", prg_name); nathan@4: exit(0); nathan@0: } nathan@0: } nathan@0: nathan@0: if(optind0) { nathan@4: VvirtRead(buffer); nathan@4: Vwrite(buffer); grandTotal+=CD_FRAMESIZE; nathan@4: secs--; nathan@4: } nathan@4: nathan@4: VcloseTrack(0); nathan@4: nathan@4: totalSize+=grandTotal; nathan@4: if(verbose) { nathan@4: char str1[16], str2[16]; nathan@4: fprintf(stderr,"%s: Dumping finished. %s written (%s on this disk)\n", nathan@4: prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal)); nathan@4: } nathan@4: nathan@4: if(multidisk==0) { nathan@4: if(cont) fprintf(stderr,"Multi-disk not enabled, ignoring continuation image(s)!\n"); nathan@4: cont=0; nathan@4: } nathan@10: else if(cont) { nathan@4: disknum++; nathan@4: diskchange(multicmd,cd_dev); nathan@4: } nathan@4: } while(cont); nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: int backup(void) nathan@4: { nathan@0: long long grandTotal=0; nathan@0: struct header_block header; nathan@4: int flags, datasize, result=0; nathan@0: nathan@0: char buffer[CD_FRAMESIZE]; nathan@0: struct data_block *db=(struct data_block *)&buffer[0]; nathan@0: nathan@4: flags=F_NONE; nathan@4: datasize=DATASIZE; nathan@4: if(crc) { flags|=F_CRC; datasize-=4; } nathan@4: nathan@4: sprintf(buffer,"%04d%02d%02d%02d%02d",curtime.tm_year+1900, nathan@4: curtime.tm_mon+1,curtime.tm_mday,curtime.tm_hour,curtime.tm_min); nathan@0: nathan@4: strncpy(header.id_str,HDR_STRING,32); header.id_str[32]=0; nathan@4: strncpy(header.vol_id,cd_label,32); header.vol_id[32]=0; nathan@4: strncpy(header.t_stamp,buffer,12); header.t_stamp[12]=0; nathan@4: header.disk_set = disknum; nathan@4: header.flags = flags; nathan@0: nathan@0: if(verbose) nathan@4: fprintf(stderr,"%s: Recording to %s, multidisk %s, CRC %s, disk %d\n", nathan@4: prg_name,VdevName(), nathan@4: multidisk?"enabled":"disabled", nathan@4: crc?"enabled":"disabled", nathan@4: disknum); nathan@4: secs=cd_len; nathan@4: VnewTrack(); nathan@4: nathan@0: memset(buffer,0,CD_FRAMESIZE); nathan@0: memcpy(buffer,&header,sizeof(struct header_block)); nathan@4: Vwrite(buffer); grandTotal+=CD_FRAMESIZE; nathan@0: nathan@0: do { nathan@4: int bytes; nathan@4: nathan@4: db->flags=flags; nathan@4: db->status=0; /* this isn't the last block (for now) */ nathan@4: bytes=full_read(0,&buffer[DBSIZE],datasize); nathan@4: if(bytes!=datasize) db->status=1; /* EOF, this is the last block */ nathan@4: db->datasize=htons(bytes); nathan@4: nathan@4: if(cd_avail<(CD_FRAMESIZE*2)) { /* less than 2 block free */ nathan@4: if(db->status==0) { /* if not last block, mark disk as full */ nathan@4: db->status=2; nathan@4: result=1; nathan@4: } nathan@4: } nathan@4: if(crc) { nathan@4: int l=crc32(buffer,bytes+DBSIZE); nathan@4: *((unsigned long *)(&buffer[CD_FRAMESIZE-4]))=l; nathan@4: } nathan@4: Vwrite(buffer); grandTotal+=CD_FRAMESIZE; nathan@0: } while(db->status==0); nathan@0: nathan@4: if(dvd && cd_avail>=CD_FRAMESIZE) { /* pad up the track with zeros */ nathan@4: memset(buffer,0,CD_FRAMESIZE); nathan@4: if(verbose) fprintf(stderr,"%s: padding up the track\n",prg_name); nathan@4: while(cd_avail>=CD_FRAMESIZE) Vwrite(buffer); nathan@4: } nathan@4: nathan@4: VcloseTrack(result); nathan@0: nathan@0: totalSize+=grandTotal; nathan@4: if(verbose) { nathan@4: char str1[16], str2[16]; nathan@4: fprintf(stderr,"%s: Recording finished. %s written (%s on this disk)\n", nathan@4: prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal)); nathan@4: } nathan@4: return result; nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: int main(int argc, char *argv[]) nathan@4: { nathan@4: int result, loop; nathan@4: time_t curtime_t; nathan@4: nathan@4: curtime_t=time(0); curtime=*localtime(&curtime_t); nathan@4: parse_cmdline(argc,argv); nathan@4: nathan@4: if(virt_dump) { nathan@4: dump(); nathan@4: } nathan@4: else { nathan@4: do { nathan@4: do { nathan@4: autosize(); nathan@4: Vopen(0); result=VreadToc(0); VprintSpace(); nathan@4: loop=1; nathan@4: nathan@4: if(disknum>1 && result!=0) { nathan@4: Vclose(); nathan@4: fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name); nathan@4: diskchange(multicmd,cd_dev); nathan@4: } nathan@4: else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) { nathan@4: Vclose(); nathan@4: if(multidisk) { nathan@4: fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name); nathan@4: diskchange(multicmd,cd_dev); nathan@4: } nathan@4: else serror("Not enough free space on disk"); nathan@4: } nathan@4: else loop=0; nathan@4: } while(loop); nathan@4: nathan@4: result=backup(); nathan@4: if(result==1) { nathan@4: if(multidisk==0) serror("Disk full, multi-disk not enabled. Aborting"); nathan@4: nathan@4: disknum++; nathan@4: if(!VisRegular()) diskchange(multicmd,cd_dev); nathan@4: } nathan@4: } while(result!=0); nathan@4: } nathan@0: return 0; nathan@0: }