cdbackup.c
author nathan
Sat, 29 Dec 2007 15:28:06 +0100
branchtrunk
changeset 8 a306b5e43b44
parent 6 6262df5a6216
child 10 79f97a86b31a
permissions -rw-r--r--
release 0.7.0
     1 /* cdbackup.c
     2 Copyright (c) 2000-2004 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 #define _GNU_SOURCE
    28 
    29 #include <stdlib.h>
    30 #include <stdio.h>
    31 #include <stdarg.h>
    32 #include <unistd.h>
    33 #include <fcntl.h>
    34 #include <string.h>
    35 #include <time.h>
    36 #include <errno.h>
    37 #include <sys/wait.h>
    38 #include <sys/ioctl.h>
    39 #include <netinet/in.h>
    40 
    41 #ifndef sun
    42 #include <getopt.h>
    43 #endif
    44 
    45 #include "cdbackup.h"
    46 #include "cdrom.h"
    47 #include "virtual.h"
    48 #include "misc.h"
    49 #include "debug.h"
    50 #include "version.h"
    51 
    52 /* defaults */
    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";
    57 int   cd_speed =4;
    58 long  cd_len   =-1;                    /* blocks */
    59 int   padsize  =15;                    /* blocks */
    60 int   multidisk=0;
    61 char *multicmd =0;
    62 int   verbose  =0;
    63 int   xamode2  =0;
    64 int   crc      =1;
    65 int   debug    =0;
    66 int   virtual  =0;
    67 char *virt_name=0;
    68 int   virt_dump=0;
    69 int   dvd      =0;
    70 
    71 char **cdrec_opt=0;
    72 int    cdrec_opt_count=0;
    73 
    74 long long totalSize=0;
    75 int disknum=1;
    76 int secs;
    77 struct tm curtime;  /* global, so multi-disks get all the same time */
    78 int auto_size=0;
    79 
    80 /****************************************************************************/
    81 
    82 char *make_arg(const char *format, ...)
    83 {
    84   char *ptr;
    85   va_list ap;
    86   va_start(ap,format);
    87   if(vasprintf(&ptr,format,ap)<0) serror("No memory for cdrecord args\n");
    88   va_end(ap);
    89   return ptr;
    90 }
    91 
    92 void start_cdrecord(void)
    93 {
    94   char **args, **p, *exname;
    95   int l;
    96   
    97   if(!(p=args=calloc(cdrec_opt_count+10,sizeof(char *))))
    98     serror("No memory for cdrecord args\n");
    99 
   100   if(dvd) exname="dvdrecord"; else exname="cdrecord";
   101   *p++=exname;
   102 
   103   if(virt_dump || dvd) {
   104     *p++="-dao";
   105     *p++=make_arg("tsize=%ds",secs);
   106     }
   107   else {
   108     *p++="-multi";
   109     *p++=make_arg("padsize=%ds",padsize);
   110     }
   111 
   112   *p++=make_arg("speed=%d",cd_speed);
   113   *p++=make_arg("dev=%s",cdr_dev);
   114 
   115   for(l=0 ; l<cdrec_opt_count ; l++) *p++=cdrec_opt[l];
   116 
   117   if(xamode2 && !dvd) *p++="-xa2"; else *p++="-data";
   118   *p++="-";
   119   *p++=0;
   120 
   121   if(debug) {
   122     fprintf(stderr,"%s: cdrecord command:",prg_name);
   123     for(p=args ; *p ; p++) fprintf(stderr," %s",*p);
   124     fprintf(stderr,"\n");
   125     }
   126 
   127   execvp(exname,args);
   128   error("Exec failed (cdrecord)");
   129 }
   130 
   131 long atip_cdrecord(void)
   132 {
   133   char *cmd;
   134   FILE *p;
   135   long size=-1;
   136   
   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);
   139 
   140   p=popen(cmd,"r");
   141   if(!p) fprintf(stderr,"%s: atip command failed\n",prg_name);
   142   else {
   143     char buff[256];
   144     while(fgets(buff,sizeof(buff),p)) {
   145       if(dvd) {
   146          /* DVD-R */
   147 	 if(!strncmp(buff,"rzone size:",11)) size=strtol(&buff[11],NULL,10);
   148 	 /* DVD+R */
   149 	 else if(!strncmp(buff,"phys size:...",13)) size=strtol(&buff[13],NULL,10);
   150 	 }
   151       else if(!strncmp(buff,"  ATIP start of lead out:",25)) size=strtol(&buff[25],NULL,10);
   152       }
   153     }
   154   pclose(p);
   155   free(cmd);
   156   if(size>0 && verbose) {
   157     char buff[16];
   158     fprintf(stderr,"%s: auto-detected media size %s (%ld blocks)\n",prg_name,FlexSize(buff,(long long)size*CD_FRAMESIZE),size);
   159     }
   160   return size;
   161 }
   162 
   163 /****************************************************************************/
   164 
   165 void parse_cmdline(char argc, char *argv[]) 
   166 {
   167   int i;
   168   char *val;
   169 
   170   /* get some default from the environment */
   171   val=getenv("CDR_DEVICE");
   172   if(val) {
   173     cdr_dev=strdup(val);
   174     DEBUG("cdbackup: using recording device %s from CDR_DEVICE\n",cdr_dev);
   175     }
   176   val=getenv("CDR_SPEED");
   177   if(val) {
   178     cd_speed=strtol(val,NULL,10);
   179     DEBUG("cdbackup: using speed %d from CDR_SPEED\n",cd_speed);
   180     }
   181   
   182   while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXDCi:wR"))>0) {
   183     switch (i) {
   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");
   189                  exit(0);
   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;
   200        case 'R': dvd=1;
   201                  DEBUG("cdbackup: DVD mode enabled\n");
   202                  break;
   203        case 'D': verbose=1; debug=1; 
   204                  DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n");
   205                  break;
   206        case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
   207                  break;
   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");
   210 	         break;
   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");
   213 	         break;
   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"
   230                          "  -v             be verbose\n"
   231                          "  -D             enable DEBUG output\n"
   232                          "  -V             prints version & exits\n"
   233                          "  --             pass rest of commandline to cdrecord\n"
   234                          "\n", prg_name);
   235                  exit(0);
   236        }
   237     }
   238 
   239   if(optind<argc) { /* save position/count of cdrecord options */
   240     cdrec_opt_count=argc-optind;
   241     cdrec_opt=&argv[optind];
   242     }
   243     
   244   if(cd_len<0) {
   245     auto_size=1;
   246     if(virtual && !virt_dump) serror("Can't auto-detect media size in virtual mode. Use option -l to set media size\n");
   247     }
   248   if(virtual && dvd && !virt_dump) {
   249      fprintf(stderr,"Option -R ignored in virtual mode\n");
   250      dvd=0;
   251      }
   252   if(dvd) {
   253     if(xamode2) fprintf(stderr,"Option -X ignored in DVD mode\n");
   254     padsize=0;
   255     }
   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");
   258 }
   259 
   260 /****************************************************************************/
   261 
   262 void autosize(void)
   263 {
   264   if(auto_size) {
   265     cd_len=atip_cdrecord();
   266     if(cd_len<0) serror("Media size detection failed. Use option -l to set media size\n");
   267     }
   268 }
   269 
   270 /****************************************************************************/
   271 
   272 void dump(void)
   273 {
   274   int n, cont;
   275   char buffer[CD_FRAMESIZE];
   276   long long grandTotal;
   277 
   278   do {
   279     int change;
   280     do {
   281       autosize();
   282       virtual=1;
   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();
   288 
   289       virtual=0; change=0;
   290       Vopen(0); n=VreadToc(0); VprintSpace();
   291       if(n!=0) {
   292         fprintf(stderr,"Can't dump to non-empty disk! Try another disk\n");
   293         change=1;
   294         }
   295 
   296       if(change) {
   297         Vclose();
   298         diskchange(multicmd,cd_dev);
   299         }
   300       } while(change);
   301 
   302     if(verbose)
   303       fprintf(stderr,"%s: Dumping image (%d blocks) to %s\n",prg_name,secs,VdevName());
   304     VnewTrack();
   305 
   306     grandTotal=0;
   307     while(secs>0) {
   308       VvirtRead(buffer);
   309       Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
   310       secs--;
   311       }
   312 
   313     VcloseTrack(0);
   314 
   315     totalSize+=grandTotal;
   316     if(verbose) {
   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));
   320       }
   321 
   322     if(multidisk==0) {
   323       if(cont) fprintf(stderr,"Multi-disk not enabled, ignoring continuation image(s)!\n");
   324       cont=0;
   325       }
   326     else {
   327       disknum++;
   328       diskchange(multicmd,cd_dev);
   329       }
   330     } while(cont);
   331 }
   332 
   333 /****************************************************************************/
   334 
   335 int backup(void)
   336 {
   337   long long grandTotal=0;
   338   struct header_block header;
   339   int flags, datasize, result=0;
   340 
   341   char buffer[CD_FRAMESIZE];
   342   struct data_block *db=(struct data_block *)&buffer[0];
   343 
   344   flags=F_NONE;
   345   datasize=DATASIZE;
   346   if(crc) { flags|=F_CRC; datasize-=4; }
   347 
   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);
   350   
   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;
   356 
   357   if(verbose)
   358     fprintf(stderr,"%s: Recording to %s, multidisk %s, CRC %s, disk %d\n",
   359             prg_name,VdevName(),
   360             multidisk?"enabled":"disabled",
   361             crc?"enabled":"disabled",
   362             disknum); 
   363   secs=cd_len;
   364   VnewTrack();
   365 
   366   memset(buffer,0,CD_FRAMESIZE);
   367   memcpy(buffer,&header,sizeof(struct header_block));
   368   Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
   369 
   370   do {
   371     int bytes;
   372 
   373     db->flags=flags;
   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);
   378 
   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 */
   381         db->status=2;
   382         result=1;
   383         }
   384       }
   385     if(crc) {
   386       int l=crc32(buffer,bytes+DBSIZE);
   387       *((unsigned long *)(&buffer[CD_FRAMESIZE-4]))=l;
   388       }
   389     Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
   390     } while(db->status==0);
   391 
   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);
   396     }
   397 
   398   VcloseTrack(result);
   399 
   400   totalSize+=grandTotal;
   401   if(verbose) {
   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));
   405     }
   406   return result;
   407 }
   408 
   409 /****************************************************************************/
   410 
   411 int main(int argc, char *argv[]) 
   412 {
   413   int result, loop;
   414   time_t curtime_t;
   415 
   416   curtime_t=time(0); curtime=*localtime(&curtime_t);
   417   parse_cmdline(argc,argv);
   418 
   419   if(virt_dump) {
   420     dump();
   421     }
   422   else {
   423     do {
   424       do {
   425         autosize();
   426         Vopen(0); result=VreadToc(0); VprintSpace();
   427         loop=1;
   428 
   429         if(disknum>1 && result!=0) {
   430           Vclose();
   431           fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name);
   432           diskchange(multicmd,cd_dev);
   433           }
   434         else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
   435           Vclose();
   436           if(multidisk) {
   437             fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name);
   438             diskchange(multicmd,cd_dev);
   439             }
   440           else serror("Not enough free space on disk");
   441           }
   442         else loop=0;
   443         } while(loop);
   444 
   445       result=backup();
   446       if(result==1) {
   447         if(multidisk==0) serror("Disk full, multi-disk not enabled. Aborting");
   448 
   449         disknum++;
   450         if(!VisRegular()) diskchange(multicmd,cd_dev);
   451         }
   452       } while(result!=0);
   453     }
   454   return 0;
   455 }