/* Synchronise a local volume based on difference between to snapshot on the drsnap server. We assume the local volume is in sync with a given snapshot and we want to move it to another. */ #include #include #include #include #include #include #include #include #include #include "drraid.h" static int drsync_dodata( const char *host, const char *port, const char *rdev, const char *ldev, const SUPERBLOCK &fromsb, const SUPERBLOCK &tosb, const bool test, unsigned long long sb_offset) // RAID superblock offset // Must not be overwritten { glocal const SUPERBLOCK *fromsb = &fromsb; glocal const SUPERBLOCK *tosb = &tosb; glocal bool test = test; glocal unsigned long long sb_offset = sb_offset; glocal int ret = -1; glocal int rfd = open (rdev,O_RDONLY,0); glocal bool errors = false; glocal FILE *flog; glocal.flog = fopen ("/tmp/drsync.log","w"); if (glocal.rfd == -1){ tlmp_error ("Can't open source device %s (%s)",rdev ,strerror(errno)); }else{ glocal int lfd = open (ldev,O_RDWR,0); glocal int chunk_size = -1; if (glocal.rfd == -1){ tlmp_error ("Can't open target device %s (%s)",ldev ,strerror(errno)); }else{ glocal unsigned long long total = 0; glocal unsigned long blocks = 0; (host,port,10); sendf ("diff %Lu %Lu %Lu %Lu\n" ,glocal.fromsb->utime,glocal.fromsb->events ,glocal.tosb->utime,glocal.tosb->events); if (strncmp(line,"#CHUNK:",7)==0){ glocal.chunk_size = atoi(line+7); }else if (strncmp(line,"***",3)==0){ tlmp_error ("%s",line); glocal.errors = true; }else if (strcmp(line,"Done")==0){ glocal.ret = glocal.errors ? -1 : 0; end = true; }else if (isdigit(line[0])){ if (glocal.chunk_size == -1){ tlmp_error ("Protocol error: No chunk size specified"); end = true; }else{ int block = atoi(line); unsigned long long offset = glocal.chunk_size * (unsigned long long)block; int chunk_size = glocal.chunk_size; if (offset+chunk_size > glocal.sb_offset){ // Avoid writing over the local superblock chunk_size = (int)(glocal.sb_offset - offset); } fprintf (glocal.flog,"%Lu %d\n",offset,chunk_size); if (!glocal.test && chunk_size > 0){ char buf[chunk_size]; if (lseek64(glocal.rfd,offset,SEEK_SET)==-1){ tlmp_error ("Can't seek remote file to position %Lu (%s)" ,offset,strerror(errno)); end = true; }else if (read(glocal.rfd,buf,chunk_size) !=chunk_size){ tlmp_error ("Can't read remote file to position %Lu (%s)" ,offset,strerror(errno)); end = true; }else if (lseek64(glocal.lfd,offset,SEEK_SET)==-1){ tlmp_error ("Can't seek local file to position %Lu (%s)" ,offset,strerror(errno)); end = true; }else if (write(glocal.lfd,buf,chunk_size) !=chunk_size){ tlmp_error ("Can't write to local file at position %Lu (%s)" ,offset,strerror(errno)); end = true; }else{ glocal.blocks ++; glocal.total += chunk_size; } }else{ glocal.blocks ++; glocal.total += chunk_size; } } } close (glocal.lfd); if (glocal.test) printf ("**** Test mode ****\n"); printf ("%'lu blocks updated\n",glocal.blocks); printf ("%'Lu Kbytes updated\n",glocal.total/1024); } close (glocal.rfd); } fclose (glocal.flog); return glocal.ret; } static int drsync_checksb( const char *rdev, const char *ldev, SUPERBLOCK &sb_src, SUPERBLOCK &sb_dst, unsigned long long &sb_offset) { int ret = -1; unsigned long long offset; if (drraid_findsb(rdev,sb_src,offset)!=-1 && drraid_findsb(ldev,sb_dst,sb_offset)!=-1 && drraid_compat(rdev,sb_src,ldev,sb_dst)!=-1){ ret = 0; } return ret; } int main (int argc, char *argv[]) { glocal int ret = -1; glocal const char *host = NULL; glocal const char *port = "10001"; glocal const char *rdev = "/dev/nbd0"; glocal const char *ldev = NULL; glocal bool test = false; //glocal int fromrev = -1; //glocal int torev = -1; glocal.ret = (argc,argv); #if 0 setgrouparg ("Snapshots"); setarg ('f',"fromrev","Revision number of the starting snapshot" ,glocal.fromrev,true); setarg ('t',"torev","Revision number of the target snapshot" ,glocal.torev,true); #endif setgrouparg ("Devices"); setarg ('R',"sourcedev","Device to read",glocal.rdev,false); setarg ('T',"targetdev","Device to update",glocal.ldev,true); setgrouparg ("Networking"); setarg ('h',"host","Host name of the drsnapd server" ,glocal.host,true); setarg ('p',"port","TCP control port of the drsnapd server" ,glocal.port,false); setarg ('e',"test","Show stats, do not update the target device" ,glocal.test,false); fprintf (stderr,"%s\n",msg); int ret = -1; SUPERBLOCK sb_rdev,sb_ldev; unsigned long long sb_offset; if (drsync_checksb (glocal.rdev,glocal.ldev,sb_rdev,sb_ldev,sb_offset)==-1){ tlmp_error ("Incompatible/Unrelated RAID1 devices, can't sync\n"); }else if (drraid_insync(sb_rdev,sb_ldev)){ printf ("Device already in sync, nothing to do\n"); ret = 0; }else if (drsync_dodata(glocal.host,glocal.port ,glocal.rdev,glocal.ldev ,sb_ldev,sb_rdev,glocal.test,sb_offset)==-1){ tlmp_error ("Failed to synchronise the data, aborting\n"); }else if (glocal.test){ ret = 0; }else if (drraid_sync(sb_rdev,sb_ldev,glocal.ldev,sb_offset)==-1){ tlmp_error ("Failed to synchronise the superblock, aborting\n"); }else{ ret = 0; } return ret; return glocal.ret; }