diff options
Diffstat (limited to 'src/bsdiff-4.2/bspatch.c')
-rw-r--r-- | src/bsdiff-4.2/bspatch.c | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/bsdiff-4.2/bspatch.c b/src/bsdiff-4.2/bspatch.c new file mode 100644 index 000000000000..1386a7096213 --- /dev/null +++ b/src/bsdiff-4.2/bspatch.c @@ -0,0 +1,216 @@ +/* + .c -- Binary patcher + + Copyright 2003,2004 Colin Percival + + For the terms under which this work may be distributed, please see + the adjoining file "LICENSE". +*/ + +#ifndef BZIP2 +#define BZIP2 "/usr/bin/bzip2" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <err.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/wait.h> + +ssize_t loopread(int d,void *buf,size_t nbytes) +{ + ssize_t ptr,lenread; + + for(ptr=0;ptr<nbytes;ptr+=lenread) { + lenread=read(d,buf+ptr,nbytes-ptr); + if(lenread==0) return ptr; + if(lenread==-1) return -1; + }; + return ptr; +} + +int bz2read(int fd,off_t offset,off_t len,char * fname,pid_t * pids) +{ + int p0[2],p1[2]; + u_char * data; + + if((pipe(p0)==-1) || (pipe(p1)==-1)) err(1,NULL); + + if((pids[0]=fork())==-1) err(1,NULL); + if(pids[0]==0) { + if(close(0) || close(1) || close(p0[0]) || + close(p1[0]) || close(p1[1])) err(1,NULL); + if((data=malloc(len+1))==NULL) err(1,NULL); + if((pread(fd,data,len,offset)!=len) || close(fd)) + err(1,"%s",fname); + if((write(p0[1],data,len)!=len) || close(p0[1])) + err(1,NULL); + free(data); + _exit(0); + }; + + if((pids[1]=fork())==-1) err(1,NULL); + if(pids[1]==0) { + if(close(0) || close(1) || close(p0[1]) || + close(p1[0])) err(1,NULL); + if((dup2(p0[0],0)==-1) || close(p0[0])) err(1,NULL); + if((dup2(p1[1],1)==-1) || close(p1[1])) err(1,NULL); + if(close(fd)==-1) err(1,"%s",fname); + + execl(BZIP2,BZIP2,"-dc",NULL); + err(1,"%s",BZIP2); + }; + + if(close(p0[0]) || close(p0[1]) || close(p1[1])) err(1,NULL); + + return p1[0]; +} + +off_t offtin(u_char *buf) +{ + off_t y; + + y=buf[7]&0x7F; + y=y*256;y+=buf[6]; + y=y*256;y+=buf[5]; + y=y*256;y+=buf[4]; + y=y*256;y+=buf[3]; + y=y*256;y+=buf[2]; + y=y*256;y+=buf[1]; + y=y*256;y+=buf[0]; + + if(buf[7]&0x80) y=-y; + + return y; +} + +int main(int argc,char * argv[]) +{ + int fd,ctrlpipe,diffpipe,extrapipe; + pid_t pids[6]; + ssize_t patchsize,oldsize,newsize; + ssize_t bzctrllen,bzdatalen; + u_char header[32],buf[8]; + int version=0; + u_char *old, *new; + off_t oldpos,newpos; + off_t ctrl[3]; + off_t lenread; + off_t i; + + if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]); + + if(((fd=open(argv[3],O_RDONLY,0))<0) || + ((patchsize=lseek(fd,0,SEEK_END))==-1) || + (lseek(fd,0,SEEK_SET)!=0)) err(1,"%s",argv[3]); + if(patchsize<32) errx(1,"Corrupt patch\n"); + + /* + Ok, this is going to be messy. There are two different patch + formats which we need to support. + + The old format (pre-4.0) is: + 0 8 "QSUFDIFF" or "BSDIFF30" + 8 8 X + 16 8 Y + 24 8 sizeof(newfile) + 32 X bzip2(control block) + 32+X Y bzip2(data block) + with control block a set of pairs (x,y) meaning "seek forward + in oldfile by y bytes, and add the next x bytes to x bytes from + the data block". + + The new format (4.0) is: + 0 8 "BSDIFF40" + 8 8 X + 16 8 Y + 24 8 sizeof(newfile) + 32 X bzip2(control block) + 32+X Y bzip2(diff block) + 32+X+Y ??? bzip2(extra block) + with control block a set of triples (x,y,z) meaning "add x bytes + from oldfile to x bytes from the diff block; copy y bytes from the + extra block; seek forwards in oldfile by z bytes". + */ + + if(read(fd,header,32)!=32) err(1,"%s",argv[3]); + if(memcmp(header,"QSUFDIFF",8)==0) version=1; + if(memcmp(header,"BSDIFF30",8)==0) version=1; + if(memcmp(header,"BSDIFF40",8)==0) version=2; + + if(!version) errx(1,"Corrupt patch\n"); + + bzctrllen=offtin(header+8); + bzdatalen=offtin(header+16); + newsize=offtin(header+24); + if((bzctrllen<0) || (bzdatalen<0) || (newsize<0) || + ((version==1) && (32+bzctrllen+bzdatalen!=patchsize))) + errx(1,"Corrupt patch\n"); + + ctrlpipe=bz2read(fd,32,bzctrllen,argv[3],pids); + diffpipe=bz2read(fd,32+bzctrllen,bzdatalen,argv[3],pids+2); + if(version==2) { + extrapipe=bz2read(fd,32+bzctrllen+bzdatalen, + patchsize-(32+bzctrllen+bzdatalen),argv[3],pids+4); + }; + + if(close(fd)==-1) err(1,"%s",argv[3]); + if(((fd=open(argv[1],O_RDONLY,0))<0) || + ((oldsize=lseek(fd,0,SEEK_END))==-1) || + ((old=malloc(oldsize+1))==NULL) || + (lseek(fd,0,SEEK_SET)!=0) || + (read(fd,old,oldsize)!=oldsize) || + (close(fd)==-1)) err(1,"%s",argv[1]); + if((new=malloc(newsize+1))==NULL) err(1,NULL); + + oldpos=0;newpos=0; + while(newpos<newsize) { + for(i=0;i<=version;i++) { + if((lenread=loopread(ctrlpipe,buf,8))<0) err(1,NULL); + if(lenread<8) errx(1,"Corrupt patch\n"); + ctrl[i]=offtin(buf); + }; + + if(version==1) oldpos+=ctrl[1]; + + if(newpos+ctrl[0]>newsize) errx(1,"Corrupt patch\n"); + if((lenread=loopread(diffpipe,new+newpos,ctrl[0]))<0) + err(1,NULL); + if(lenread!=ctrl[0]) errx(1,"Corrupt patch\n"); + for(i=0;i<ctrl[0];i++) + if((oldpos+i>=0) && (oldpos+i<oldsize)) + new[newpos+i]+=old[oldpos+i]; + newpos+=ctrl[0]; + oldpos+=ctrl[0]; + + if(version==2) { + if(newpos+ctrl[1]>newsize) errx(1,"Corrupt patch\n"); + if((lenread=loopread(extrapipe,new+newpos,ctrl[1]))<0) + err(1,NULL); + if(lenread!=ctrl[1]) errx(1,"Corrupt patch\n"); + + newpos+=ctrl[1]; + oldpos+=ctrl[2]; + }; + }; + + if(loopread(ctrlpipe,buf,1)!=0) errx(1,"Corrupt patch\n"); + if(loopread(diffpipe,buf,1)!=0) errx(1,"Corrupt patch\n"); + if(version==2) + if(loopread(extrapipe,buf,1)!=0) errx(1,"Corrupt patch\n"); + + if(close(ctrlpipe) || close(diffpipe) || + ((version==2) && close(extrapipe))) + err(1,NULL); + for(i=0;i<(version+1)*2;i++) waitpid(pids[i],NULL,0); + + if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) || + (write(fd,new,newsize)!=newsize) || (close(fd)==-1)) + err(1,"%s",argv[2]); + + free(new); + free(old); + + return 0; +} |