summaryrefslogtreecommitdiff
path: root/dpkg-deb/extract.c
diff options
context:
space:
mode:
Diffstat (limited to 'dpkg-deb/extract.c')
-rw-r--r--dpkg-deb/extract.c316
1 files changed, 316 insertions, 0 deletions
diff --git a/dpkg-deb/extract.c b/dpkg-deb/extract.c
new file mode 100644
index 000000000..8c9327fa1
--- /dev/null
+++ b/dpkg-deb/extract.c
@@ -0,0 +1,316 @@
+/*
+ * dpkg-deb - construction and deconstruction of *.deb archives
+ * extract.c - extracting archives
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <iwj10@cus.cam.ac.uk>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <ctype.h>
+#include <assert.h>
+#include <ar.h>
+
+#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,"!<arch>\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 {
+ 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); }