/* * dpkg-deb - construction and deconstruction of *.deb archives * extract.c - extracting archives * * Copyright (C) 1994,1995 Ian Jackson * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, * or (at your option) any later version. * * This is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with dpkg; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "dpkg.h" #include "dpkg-deb.h" #include "myopt.h" static void movecontrolfiles(const char *thing) { char buf[200]; pid_t c1; sprintf(buf, "mv %s/* . && rmdir %s", thing, thing); if (!(c1= m_fork())) { execlp("sh","sh","-c",buf,(char*)0); ohshite("failed to exec sh -c mv foo/* &c"); } waitsubproc(c1,"sh -c mv foo/* &c",0); } static void readfail(FILE *a, const char *filename, const char *what) { if (ferror(a)) { ohshite("error reading %s from %.255s",what,filename); } else { ohshit("unexpected end of file in %s in %.255s",what,filename); } } static unsigned long parseheaderlength(const char *inh, size_t len, const char *fn, const char *what) { char lintbuf[15]; unsigned long r; char *endp; if (memchr(inh,0,len)) ohshit("file `%.250s' is corrupt - %.250s length contains nulls",fn,what); assert(sizeof(lintbuf) > len); memcpy(lintbuf,inh,len); lintbuf[len]= ' '; *strchr(lintbuf,' ')= 0; r= strtoul(lintbuf,&endp,10); if (*endp) ohshit("file `%.250s' is corrupt - bad digit (code %d) in %s",fn,*endp,what); return r; } static void skipmember(FILE *ar, const char *fn, long memberlen) { int c; memberlen += (memberlen&1); while (memberlen > 0) { if ((c= getc(ar)) == EOF) readfail(ar,fn,"skipped member data"); memberlen--; } } void extracthalf(const char *debar, const char *directory, const char *taroption, int admininfo) { char versionbuf[40]; float versionnum; char ctrllenbuf[40], *infobuf; long ctrllennum, memberlen= 0; int dummy, l= 0; pid_t c1=0,c2,c3; unsigned char *ctrlarea= 0; int p1[2], p2[2]; FILE *ar, *pi; struct stat stab; char nlc; char *cur; struct ar_hdr arh; int readfromfd, oldformat, header_done, adminmember, c; ar= fopen(debar,"r"); if (!ar) ohshite("failed to read archive `%.255s'",debar); if (fstat(fileno(ar),&stab)) ohshite("failed to fstat archive"); if (!fgets(versionbuf,sizeof(versionbuf),ar)) readfail(ar,debar,"version number"); if (!strcmp(versionbuf,"!\n")) { oldformat= 0; ctrllennum= 0; header_done= 0; for (;;) { if (fread(&arh,1,sizeof(arh),ar) != sizeof(arh)) readfail(ar,debar,"between members"); if (memcmp(arh.ar_fmag,ARFMAG,sizeof(arh.ar_fmag))) ohshit("file `%.250s' is corrupt - bad magic at end of first header",debar); memberlen= parseheaderlength(arh.ar_size,sizeof(arh.ar_size), debar,"member length"); if (memberlen<0) ohshit("file `%.250s' is corrupt - negative member length %ld",debar,memberlen); if (!header_done) { if (memcmp(arh.ar_name,"debian-binary ",sizeof(arh.ar_name))) ohshit("file `%.250s' is not a debian binary archive (try dpkg-split?)",debar); infobuf= m_malloc(memberlen+1); if (fread(infobuf,1, memberlen + (memberlen&1), ar) != memberlen + (memberlen&1)) readfail(ar,debar,"header info member"); infobuf[memberlen]= 0; cur= strchr(infobuf,'\n'); if (!cur) ohshit("archive has no newlines in header"); *cur= 0; cur= strchr(infobuf,'.'); if (!cur) ohshit("archive has no dot in version number"); *cur= 0; if (strcmp(infobuf,"2")) ohshit("archive version %.250s not understood, get newer " BACKEND, infobuf); *cur= '.'; strncpy(versionbuf,infobuf,sizeof(versionbuf)); versionbuf[sizeof(versionbuf)-1]= 0; header_done= 1; } else if (arh.ar_name[0] == '_') { /* Members with `_' are noncritical, and if we don't understand them * we skip them. */ skipmember(ar,debar,memberlen); } else { adminmember= !memcmp(arh.ar_name,ADMINMEMBER,sizeof(arh.ar_name)) ? 1 : !memcmp(arh.ar_name,DATAMEMBER,sizeof(arh.ar_name)) ? 0 : -1; if (adminmember == -1) { ohshit("file `%.250s' contains ununderstood data member %.*s, giving up", debar, (int)sizeof(arh.ar_name), arh.ar_name); } if (adminmember == 1) { if (ctrllennum != 0) ohshit("file `%.250s' contains two control members, giving up", debar); ctrllennum= memberlen; } if (!adminmember != !admininfo) { skipmember(ar,debar,memberlen); } else { break; /* Yes ! - found it. */ } } } if (admininfo >= 2) if (printf(" new debian package, version %s.\n" " size %ld bytes: control archive= %ld bytes.\n", versionbuf, (long)stab.st_size, ctrllennum) == EOF || fflush(stdout)) werr("stdout"); } else if (!strncmp(versionbuf,"0.93",4) && sscanf(versionbuf,"%f%c%d",&versionnum,&nlc,&dummy) == 2 && nlc == '\n') { oldformat= 1; l= strlen(versionbuf); if (l && versionbuf[l-1]=='\n') versionbuf[l-1]=0; if (!fgets(ctrllenbuf,sizeof(ctrllenbuf),ar)) readfail(ar,debar,"ctrl information length"); if (sscanf(ctrllenbuf,"%ld%c%d",&ctrllennum,&nlc,&dummy) !=2 || nlc != '\n') ohshit("archive has malformatted ctrl len `%s'",ctrllenbuf); if (admininfo >= 2) if (printf(" old debian package, version %s.\n" " size %ld bytes: control archive= %ld, main archive= %ld.\n", versionbuf, (long)stab.st_size, ctrllennum, (long) (stab.st_size - ctrllennum - strlen(ctrllenbuf) - l)) == EOF || fflush(stdout)) werr("stdout"); ctrlarea= malloc(ctrllennum); if (!ctrlarea) ohshite("malloc ctrlarea failed"); errno=0; if (fread(ctrlarea,1,ctrllennum,ar) != ctrllennum) readfail(ar,debar,"ctrlarea"); } else { if (!strncmp(versionbuf,"!",7)) { if (fprintf(stderr, BACKEND ": file looks like it might be an archive which has been\n" BACKEND ": corrupted by being downloaded in ASCII mode\n") == EOF) werr("stderr"); } ohshit("`%.255s' is not a debian format archive",debar); } fflush(ar); if (oldformat) { if (admininfo) { m_pipe(p1); if (!(c1= m_fork())) { close(p1[0]); if (!(pi= fdopen(p1[1],"w"))) ohshite("failed to fdopen p1 in paste"); errno=0; if (fwrite(ctrlarea,1,ctrllennum,pi) != ctrllennum) ohshit("failed to write to gzip -dc"); if (fclose(pi)) ohshit("failed to close gzip -dc"); exit(0); } close(p1[1]); readfromfd= p1[0]; } else { if (lseek(fileno(ar),l+strlen(ctrllenbuf)+ctrllennum,SEEK_SET) == -1) ohshite("failed to syscall lseek to files archive portion"); c1= -1; readfromfd= fileno(ar); } } else { m_pipe(p1); if (!(c1= m_fork())) { close(p1[0]); if (!(pi= fdopen(p1[1],"w"))) ohshite("failed to fdopen p1 in copy"); while (memberlen > 0) { if ((c= getc(ar)) == EOF) readfail(ar,debar,"member data"); if (putc(c,pi) == EOF) ohshite("failed to write to pipe in copy"); memberlen--; } if (fclose(pi) == EOF) ohshite("failed to close pipe in copy"); exit(0); } close(p1[1]); readfromfd= p1[0]; } if (taroption) m_pipe(p2); if (!(c2= m_fork())) { m_dup2(readfromfd,0); if (admininfo) close(p1[0]); if (taroption) { m_dup2(p2[1],1); close(p2[0]); close(p2[1]); } execlp(GZIP,"gzip","-dc",(char*)0); ohshite("failed to exec gzip -dc"); } if (readfromfd != fileno(ar)) close(readfromfd); close(p2[1]); if (taroption && directory) { if (chdir(directory)) { if (errno == ENOENT) { if (mkdir(directory,0777)) ohshite("failed to create directory"); if (chdir(directory)) ohshite("failed to chdir to directory after creating it"); } else { ohshite("failed to chdir to directory"); } } } if (taroption) { if (!(c3= m_fork())) { char buffer[30+2]; if(strlen(taroption) > 30) internerr(taroption); strcpy(buffer, taroption); strcat(buffer, "f"); m_dup2(p2[0],0); execlp(TAR,"tar",buffer,"-",(char*)0); ohshite("failed to exec tar"); } close(p2[0]); waitsubproc(c3,"tar",0); } waitsubproc(c2,"gzip -dc",1); if (c1 != -1) waitsubproc(c1,"paste",0); if (oldformat && admininfo) { if (versionnum == 0.931F) { movecontrolfiles(OLDOLDDEBDIR); } else if (versionnum == 0.932F || versionnum == 0.933F) { movecontrolfiles(OLDDEBDIR); } } } static void controlextractvextract(int admin, const char *taroptions, const char *const *argv) { const char *debar, *directory; if (!(debar= *argv++)) badusage("--%s needs a .deb filename argument",cipaction->olong); if (!(directory= *argv++)) { if (admin) directory= EXTRACTCONTROLDIR; else ohshit("--%s needs a target directory.\n" "Perhaps you should be using " DPKG " --install ?",cipaction->olong); } else if (*argv) { badusage("--%s takes at most two arguments (.deb and directory",cipaction->olong); } extracthalf(debar, directory, taroptions, admin); } void do_fsystarfile(const char *const *argv) { const char *debar; if (!(debar= *argv++)) badusage("--%s needs a .deb filename argument",cipaction->olong); if (*argv) badusage("--%s takes only one argument (.deb filename)",cipaction->olong); extracthalf(debar,0,0,0); } void do_control(const char *const *argv) { controlextractvextract(1, "x", argv); } void do_extract(const char *const *argv) { controlextractvextract(0, "xp", argv); } void do_vextract(const char *const *argv) { controlextractvextract(0, "xpv", argv); }