cdbackup.c
author nathan
Sat, 29 Dec 2007 15:31:08 +0100
branchtrunk
changeset 10 79f97a86b31a
parent 8 a306b5e43b44
child 12 6d3770b1a9e1
permissions -rw-r--r--
several fixes
     1 /* cdbackup.c
     2 Copyright (c) 2000-2006 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) {
    88     serror("No memory for cdrecord args\n");
    89     ptr=0;
    90     }
    91   va_end(ap);
    92   return ptr;
    93 }
    94 
    95 void start_cdrecord(void)
    96 {
    97   char **args, **p, *exname;
    98   int l;
    99   
   100   if(!(p=args=calloc(cdrec_opt_count+32,sizeof(char *))))
   101     serror("No memory for cdrecord args\n");
   102 
   103   if(dvd) exname="dvdrecord"; else exname="cdrecord";
   104   *p++=exname;
   105 
   106   if(virt_dump || dvd) {
   107     *p++="-dao";
   108     *p++=make_arg("tsize=%ds",secs);
   109     }
   110   else {
   111     *p++="-multi";
   112     *p++="-tao";
   113     *p++=make_arg("padsize=%ds",padsize);
   114     }
   115 
   116   *p++=make_arg("speed=%d",cd_speed);
   117   *p++=make_arg("dev=%s",cdr_dev);
   118 
   119   for(l=0 ; l<cdrec_opt_count ; l++) *p++=cdrec_opt[l];
   120 
   121   if(xamode2 && !dvd) *p++="-xa2"; else *p++="-data";
   122   *p++="-";
   123   *p++=0;
   124 
   125   if(debug) {
   126     fprintf(stderr,"%s: cdrecord command:",prg_name);
   127     for(p=args ; *p ; p++) fprintf(stderr," %s",*p);
   128     fprintf(stderr,"\n");
   129     }
   130 
   131   execvp(exname,args);
   132   error("Exec failed (cdrecord)");
   133 }
   134 
   135 long atip_cdrecord(void)
   136 {
   137   char *cmd;
   138   FILE *p;
   139   long size=-1;
   140   
   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);
   143 
   144   p=popen(cmd,"r");
   145   if(!p) fprintf(stderr,"%s: atip command failed\n",prg_name);
   146   else {
   147     char buff[256];
   148     while(fgets(buff,sizeof(buff),p)) {
   149       if(dvd) {
   150          /* DVD-R */
   151 	 if(!strncmp(buff,"rzone size:",11)) size=strtol(&buff[11],NULL,10);
   152 	 /* DVD+R */
   153 	 else if(!strncmp(buff,"phys size:...",13)) size=strtol(&buff[13],NULL,10);
   154 	 }
   155       else if(!strncmp(buff,"  ATIP start of lead out:",25)) size=strtol(&buff[25],NULL,10);
   156       }
   157     }
   158   pclose(p);
   159   free(cmd);
   160   if(size>0 && verbose) {
   161     char buff[16];
   162     fprintf(stderr,"%s: auto-detected media size %s (%ld blocks)\n",prg_name,FlexSize(buff,(long long)size*CD_FRAMESIZE),size);
   163     }
   164   return size;
   165 }
   166 
   167 /****************************************************************************/
   168 
   169 void parse_cmdline(char argc, char *argv[]) 
   170 {
   171   int i;
   172   char *val;
   173 
   174   /* get some default from the environment */
   175   val=getenv("CDR_DEVICE");
   176   if(val) {
   177     cdr_dev=strdup(val);
   178     DEBUG("cdbackup: using recording device %s from CDR_DEVICE\n",cdr_dev);
   179     }
   180   val=getenv("CDR_SPEED");
   181   if(val) {
   182     cd_speed=strtol(val,NULL,10);
   183     DEBUG("cdbackup: using speed %d from CDR_SPEED\n",cd_speed);
   184     }
   185   
   186   while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXDCi:wR"))>0) {
   187     switch (i) {
   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");
   193                  exit(0);
   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;
   204        case 'R': dvd=1;
   205                  DEBUG("cdbackup: DVD mode enabled\n");
   206                  break;
   207        case 'D': verbose=1; debug=1; 
   208                  DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n");
   209                  break;
   210        case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
   211                  break;
   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");
   214 	         break;
   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");
   217 	         break;
   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"
   234                          "  -v             be verbose\n"
   235                          "  -D             enable DEBUG output\n"
   236                          "  -V             prints version & exits\n"
   237                          "  --             pass rest of commandline to cdrecord\n"
   238                          "\n", prg_name);
   239                  exit(0);
   240        }
   241     }
   242 
   243   if(optind<argc) { /* save position/count of cdrecord options */
   244     cdrec_opt_count=argc-optind;
   245     cdrec_opt=&argv[optind];
   246     }
   247     
   248   if(cd_len<0) {
   249     auto_size=1;
   250     if(virtual && !virt_dump) serror("Can't auto-detect media size in virtual mode. Use option -l to set media size\n");
   251     }
   252   if(virtual && dvd && !virt_dump) {
   253      fprintf(stderr,"Option -R ignored in virtual mode\n");
   254      dvd=0;
   255      }
   256   if(dvd) {
   257     if(xamode2) fprintf(stderr,"Option -X ignored in DVD mode\n");
   258     padsize=0;
   259     }
   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");
   262 }
   263 
   264 /****************************************************************************/
   265 
   266 void autosize(void)
   267 {
   268   if(auto_size) {
   269     cd_len=atip_cdrecord();
   270     if(cd_len<0) serror("Media size detection failed. Use option -l to set media size\n");
   271     }
   272 }
   273 
   274 /****************************************************************************/
   275 
   276 void dump(void)
   277 {
   278   int n, cont;
   279   char buffer[CD_FRAMESIZE];
   280   long long grandTotal;
   281 
   282   do {
   283     int change;
   284     do {
   285       autosize();
   286       virtual=1;
   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();
   292 
   293       virtual=0; change=0;
   294       Vopen(0); n=VreadToc(0); VprintSpace();
   295       if(n!=0) {
   296         fprintf(stderr,"Can't dump to non-empty disk! Try another disk\n");
   297         change=1;
   298         }
   299 
   300       if(change) {
   301         Vclose();
   302         diskchange(multicmd,cd_dev);
   303         }
   304       } while(change);
   305 
   306     if(verbose)
   307       fprintf(stderr,"%s: Dumping image (%d blocks) to %s\n",prg_name,secs,VdevName());
   308     VnewTrack();
   309 
   310     grandTotal=0;
   311     while(secs>0) {
   312       VvirtRead(buffer);
   313       Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
   314       secs--;
   315       }
   316 
   317     VcloseTrack(0);
   318 
   319     totalSize+=grandTotal;
   320     if(verbose) {
   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));
   324       }
   325 
   326     if(multidisk==0) {
   327       if(cont) fprintf(stderr,"Multi-disk not enabled, ignoring continuation image(s)!\n");
   328       cont=0;
   329       }
   330     else if(cont) {
   331       disknum++;
   332       diskchange(multicmd,cd_dev);
   333       }
   334     } while(cont);
   335 }
   336 
   337 /****************************************************************************/
   338 
   339 int backup(void)
   340 {
   341   long long grandTotal=0;
   342   struct header_block header;
   343   int flags, datasize, result=0;
   344 
   345   char buffer[CD_FRAMESIZE];
   346   struct data_block *db=(struct data_block *)&buffer[0];
   347 
   348   flags=F_NONE;
   349   datasize=DATASIZE;
   350   if(crc) { flags|=F_CRC; datasize-=4; }
   351 
   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);
   354   
   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;
   360 
   361   if(verbose)
   362     fprintf(stderr,"%s: Recording to %s, multidisk %s, CRC %s, disk %d\n",
   363             prg_name,VdevName(),
   364             multidisk?"enabled":"disabled",
   365             crc?"enabled":"disabled",
   366             disknum); 
   367   secs=cd_len;
   368   VnewTrack();
   369 
   370   memset(buffer,0,CD_FRAMESIZE);
   371   memcpy(buffer,&header,sizeof(struct header_block));
   372   Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
   373 
   374   do {
   375     int bytes;
   376 
   377     db->flags=flags;
   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);
   382 
   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 */
   385         db->status=2;
   386         result=1;
   387         }
   388       }
   389     if(crc) {
   390       int l=crc32(buffer,bytes+DBSIZE);
   391       *((unsigned long *)(&buffer[CD_FRAMESIZE-4]))=l;
   392       }
   393     Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
   394     } while(db->status==0);
   395 
   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);
   400     }
   401 
   402   VcloseTrack(result);
   403 
   404   totalSize+=grandTotal;
   405   if(verbose) {
   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));
   409     }
   410   return result;
   411 }
   412 
   413 /****************************************************************************/
   414 
   415 int main(int argc, char *argv[]) 
   416 {
   417   int result, loop;
   418   time_t curtime_t;
   419 
   420   curtime_t=time(0); curtime=*localtime(&curtime_t);
   421   parse_cmdline(argc,argv);
   422 
   423   if(virt_dump) {
   424     dump();
   425     }
   426   else {
   427     do {
   428       do {
   429         autosize();
   430         Vopen(0); result=VreadToc(0); VprintSpace();
   431         loop=1;
   432 
   433         if(disknum>1 && result!=0) {
   434           Vclose();
   435           fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name);
   436           diskchange(multicmd,cd_dev);
   437           }
   438         else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
   439           Vclose();
   440           if(multidisk) {
   441             fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name);
   442             diskchange(multicmd,cd_dev);
   443             }
   444           else serror("Not enough free space on disk");
   445           }
   446         else loop=0;
   447         } while(loop);
   448 
   449       result=backup();
   450       if(result==1) {
   451         if(multidisk==0) serror("Disk full, multi-disk not enabled. Aborting");
   452 
   453         disknum++;
   454         if(!VisRegular()) diskchange(multicmd,cd_dev);
   455         }
   456       } while(result!=0);
   457     }
   458   return 0;
   459 }