summaryrefslogtreecommitdiffstats
path: root/extractcontrol.c
diff options
context:
space:
mode:
Diffstat (limited to 'extractcontrol.c')
-rw-r--r--extractcontrol.c458
1 files changed, 458 insertions, 0 deletions
diff --git a/extractcontrol.c b/extractcontrol.c
new file mode 100644
index 0000000..a9df08a
--- /dev/null
+++ b/extractcontrol.c
@@ -0,0 +1,458 @@
+/* This file is part of "reprepro"
+ * Copyright (C) 2003,2007 Bernhard R. Link
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+#include <config.h>
+
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include "error.h"
+#include "filecntl.h"
+#include "readtextfile.h"
+#include "debfile.h"
+#include "chunks.h"
+
+#ifdef HAVE_LIBARCHIVE
+#error Why did this file got compiled instead of debfile.c?
+#endif
+// **********************************************************************
+// * This is a very simple implementation calling ar and tar, which
+// * is only used with --without-libarchive or when no libarchive was
+// * found.
+// **********************************************************************
+
+static retvalue try_extractcontrol(char **control, const char *debfile, bool brokentar) {
+ int pipe_1[2];
+ int pipe_2[2];
+ int ret;
+ pid_t ar, tar, pid;
+ int status;
+ char *controlchunk;
+
+ retvalue result, r;
+
+ result = RET_OK;
+
+ ret = pipe(pipe_1);
+ if (ret < 0) {
+ int e = errno;
+ fprintf(stderr, "Error %d creating pipe: %s\n", e, strerror(e));
+ return RET_ERRNO(e);
+ }
+
+ ret = pipe(pipe_2);
+ if (ret < 0) {
+ int e = errno;
+ close(pipe_1[0]); close(pipe_1[1]);
+ fprintf(stderr, "Error %d creating pipe: %s\n", e, strerror(e));
+ return RET_ERRNO(e);
+ }
+
+ ar = fork();
+ if (ar < 0) {
+ int e = errno;
+ fprintf(stderr, "Error %d forking: %s\n", e, strerror(e));
+ result = RET_ERRNO(e);
+ close(pipe_1[0]); close(pipe_1[1]);
+ close(pipe_2[0]); close(pipe_2[1]);
+ return result;
+ }
+
+ if (ar == 0) {
+ int e;
+ /* calling ar */
+ if (dup2(pipe_1[1], 1) < 0)
+ exit(255);
+ close(pipe_1[0]); close(pipe_1[1]);
+ close(pipe_2[0]); close(pipe_2[1]);
+ //TODO without explicit path
+ ret = execl("/usr/bin/ar",
+ "ar", "p", debfile, "control.tar.gz",
+ ENDOFARGUMENTS);
+ e = errno;
+ fprintf(stderr, "ar call failed with error %d: %s\n",
+ e, strerror(e));
+ exit(254);
+ }
+
+ tar = fork();
+ if (tar < 0) {
+ int e = errno;
+ result = RET_ERRNO(e);
+ fprintf(stderr, "Error %d forking: %s\n", e, strerror(e));
+ close(pipe_1[0]); close(pipe_1[1]);
+ close(pipe_2[0]); close(pipe_2[1]);
+ tar = -1;
+ } else if (tar == 0) {
+ int e;
+ /* calling tar */
+ if (dup2(pipe_1[0], 0) < 0)
+ exit(255);
+ if (dup2(pipe_2[1], 1) < 0)
+ exit(255);
+ close(pipe_1[0]); close(pipe_1[1]);
+ close(pipe_2[0]); close(pipe_2[1]);
+ //TODO without explicit path
+ execl("/bin/tar", "tar", "-xOzf", "-",
+ brokentar?"control":"./control",
+ ENDOFARGUMENTS);
+ e = errno;
+ fprintf(stderr, "tar call failed with error %d: %s\n",
+ e, strerror(e));
+ exit(254);
+
+ }
+
+ close(pipe_1[0]); close(pipe_1[1]);
+ markcloseonexec(pipe_2[0]); close(pipe_2[1]);
+
+ controlchunk = NULL;
+
+ /* read data: */
+ if (RET_IS_OK(result)) {
+ size_t len, controllen;
+ const char *afterchanges;
+
+ r = readtextfilefd(pipe_2[0],
+ brokentar?
+"output from ar p <debfile> control.tar.gz | tar -xOzf - control":
+"output from ar p <debfile> control.tar.gz | tar -xOzf - ./control",
+ &controlchunk, &controllen);
+ if (RET_IS_OK(r)) {
+ len = chunk_extract(controlchunk,
+ controlchunk, controllen,
+ false, &afterchanges);
+ if (len == 0)
+ r = RET_NOTHING;
+ if (*afterchanges != '\0') {
+ fprintf(stderr,
+"Unexpected empty line in control information within '%s'\n"
+"(obtained via 'ar p %s control.tar.gz | tar -XOzf - %scontrol')\n",
+ debfile, debfile,
+ brokentar?"":"./");
+ free(controlchunk);
+ controlchunk = NULL;
+ r = RET_ERROR;
+ }
+ }
+ if (r == RET_NOTHING) {
+ free(controlchunk);
+ controlchunk = NULL;
+ fprintf(stderr,
+"No control information found in .deb!\n");
+ /* only report error now,
+ * if we haven't try everything yet */
+ if (brokentar)
+ r = RET_ERROR_MISSING;
+ }
+ RET_UPDATE(result, r);
+
+ }
+
+ while (ar != -1 || tar != -1) {
+ pid=wait(&status);
+ if (pid < 0) {
+ if (errno != EINTR)
+ RET_UPDATE(result, RET_ERRNO(errno));
+ } else {
+ if (pid == ar) {
+ ar = -1;
+ if (!WIFEXITED(status)) {
+ fprintf(stderr,
+"Ar exited unnaturally!\n");
+ result = RET_ERROR;
+ } else if (WEXITSTATUS(status) != 0) {
+ fprintf(stderr,
+"Error from ar for '%s': %d\n", debfile, WEXITSTATUS(status));
+ result = RET_ERROR;
+ }
+ } else if (pid == tar) {
+ tar = -1;
+ if (!WIFEXITED(status)) {
+ fprintf(stderr,
+"Tar exited unnaturally!\n");
+ result = RET_ERROR;
+ } else if (!brokentar && WEXITSTATUS(status) == 2) {
+ if (RET_IS_OK(result))
+ result = RET_NOTHING;
+ } else if (WEXITSTATUS(status) != 0) {
+ fprintf(stderr,
+"Error from tar for control.tar.gz within '%s': %d\n",
+ debfile,
+ WEXITSTATUS(status));
+ result = RET_ERROR;
+ }
+ } else {
+ // WTH?
+ fprintf(stderr,
+"Who is %d, and why does this bother me?\n", (int)pid);
+ }
+ }
+
+ }
+ if (RET_IS_OK(result)) {
+ if (controlchunk == NULL)
+ /* we got not data but tar gave not error.. */
+ return RET_ERROR_MISSING;
+ else
+ *control = controlchunk;
+ } else
+ free(controlchunk);
+ return result;
+}
+
+retvalue extractcontrol(char **control, const char *debfile) {
+ retvalue r;
+
+ r = try_extractcontrol(control, debfile, false);
+ if (r != RET_NOTHING)
+ return r;
+ /* perhaps the control.tar.gz is packaged by hand wrongly,
+ * try again: */
+ r = try_extractcontrol(control, debfile, true);
+ if (RET_IS_OK(r)) {
+ fprintf(stderr,
+"WARNING: '%s' contains a broken/unusual control.tar.gz.\n"
+"reprepro was able to work around this but other tools or versions might not.\n",
+ debfile);
+ }
+ assert (r != RET_NOTHING);
+ return r;
+}
+
+retvalue getfilelist(/*@out@*/char **filelist, /*@out@*/size_t *size, const char *debfile) {
+ fprintf(stderr,
+"Extraction of file list without libarchive currently not implemented.\n");
+ return RET_ERROR;
+#if 0
+ int pipe_1[2];
+ int pipe_2[2];
+ int ret;
+ pid_t ar, tar, pid;
+ int status;
+ struct filelistcompressor c;
+ size_t last = 0;
+ retvalue result;
+
+#error this still needs to be reimplemented...
+ result = filelistcompressor_setup(&c);
+ if (RET_WAS_ERROR(result))
+ return result;
+
+ result = RET_OK;
+
+ ret = pipe(pipe_1);
+ if (ret < 0) {
+ int e = errno;
+ fprintf(stderr, "Error %d creating pipe: %s\n", e, strerror(e));
+ filelistcompressor_cancel(&c);
+ return RET_ERRNO(e);
+ }
+
+ ret = pipe(pipe_2);
+ if (ret < 0) {
+ int e = errno;
+ fprintf(stderr, "Error %d creating pipe: %s\n", e, strerror(e));
+ close(pipe_1[0]); close(pipe_1[1]);
+ filelistcompressor_cancel(&c);
+ return RET_ERRNO(e);
+ }
+
+ ar = fork();
+ if (ar < 0) {
+ int e = errno;
+ fprintf(stderr, "Error %d forking: %s\n", e, strerror(e));
+ result = RET_ERRNO(e);
+ close(pipe_1[0]); close(pipe_1[1]);
+ close(pipe_2[0]); close(pipe_2[1]);
+ filelistcompressor_cancel(&c);
+ return result;
+ }
+
+ if (ar == 0) {
+ int e;
+ /* calling ar */
+ if (dup2(pipe_1[1], 1) < 0)
+ exit(255);
+ close(pipe_1[0]); close(pipe_1[1]);
+ close(pipe_2[0]); close(pipe_2[1]);
+ //TODO without explicit path
+ ret = execl("/usr/bin/ar",
+ "ar", "p", debfile, "data.tar.gz",
+ ENDOFARGUMENTS);
+ e = errno;
+ fprintf(stderr, "ar call failed with error %d: %s\n",
+ e, strerror(e));
+ exit(254);
+ }
+
+ tar = fork();
+ if (tar < 0) {
+ int e = errno;
+ result = RET_ERRNO(e);
+ fprintf(stderr, "Error %d forking: %s\n", e, strerror(e));
+ close(pipe_1[0]); close(pipe_1[1]);
+ close(pipe_2[0]); close(pipe_2[1]);
+ tar = -1;
+ } else if (tar == 0) {
+ int e;
+ /* calling tar */
+ if (dup2(pipe_1[0], 0) < 0)
+ exit(255);
+ if (dup2(pipe_2[1], 1) < 0)
+ exit(255);
+ close(pipe_1[0]); close(pipe_1[1]);
+ close(pipe_2[0]); close(pipe_2[1]);
+ //TODO without explicit path
+ execl("/bin/tar", "tar", "-tzf", "-", ENDOFARGUMENTS);
+ e = errno;
+ fprintf(stderr, "tar call failed with error %d: %s\n",
+ e, strerror(e));
+ exit(254);
+
+ }
+
+ close(pipe_1[0]); close(pipe_1[1]);
+ close(pipe_2[1]);
+
+ /* read data: */
+ if (RET_IS_OK(result)) do {
+ ssize_t bytes_read;
+ size_t ignore;
+
+ if (listsize <= len + 512) {
+ char *n;
+
+ listsize = len + 1024;
+ n = realloc(list, listsize);
+ if (FAILEDTOALLOC(n)) {
+ result = RET_ERROR_OOM;
+ break;
+ }
+ list = n;
+ }
+
+ ignore = 0;
+ bytes_read = read(pipe_2[0], list+len, listsize-len-1);
+ if (bytes_read < 0) {
+ int e = errno;
+ fprintf(stderr, "Error %d reading from pipe: %s\n",
+ e, strerror(e));
+ result = RET_ERRNO(e);
+ break;
+ } else if (bytes_read == 0)
+ break;
+ else while (bytes_read > 0) {
+ if (list[len] == '\0') {
+ fprintf(stderr,
+"Unexpected NUL character from tar while getting file list from %s!\n", debfile);
+ result = RET_ERROR;
+ break;
+ } else if (list[len] == '\n') {
+ if (len > last+ignore && list[len-1] != '/') {
+ list[len] = '\0';
+ len++;
+ bytes_read--;
+ memmove(list+last, list+last+ignore,
+ 1+len-last-ignore);
+ last = len-ignore;
+ } else {
+ len++;
+ bytes_read--;
+ ignore = len-last;
+ }
+ } else if (list[len] == '.' && len == last+ignore) {
+ len++; ignore++;
+ bytes_read--;
+ } else if (list[len] == '/' && len == last+ignore) {
+ len++; ignore++;
+ bytes_read--;
+ } else {
+ len++;
+ bytes_read--;
+ }
+ }
+ if (ignore > 0) {
+ if (len <= last+ignore)
+ len = last;
+ else {
+ memmove(list+last, list+last+ignore,
+ 1+len-last-ignore);
+ len -= ignore;
+ }
+ }
+ } while (true);
+ if (len != last) {
+ fprintf(stderr,
+"WARNING: unterminated output from tar pipe while extracting file list of %s\n", debfile);
+ list[len] = '\0';
+ fprintf(stderr, "The item '%s' might got lost.\n",
+ list+last);
+ result = RET_ERROR;
+ } else {
+ char *n = realloc(list, len+1);
+ if (FAILEDTOALLOC(n))
+ result = RET_ERROR_OOM;
+ else {
+ list = n;
+ list[len] = '\0';
+ }
+ }
+ close(pipe_2[0]);
+
+ while (ar != -1 || tar != -1) {
+ pid=wait(&status);
+ if (pid < 0) {
+ if (errno != EINTR)
+ RET_UPDATE(result, RET_ERRNO(errno));
+ } else {
+ if (pid == ar) {
+ ar = -1;
+ if (!WIFEXITED(status) ||
+ WEXITSTATUS(status) != 0) {
+ fprintf(stderr,
+"Error from ar for '%s': %d\n", debfile, WEXITSTATUS(status));
+ result = RET_ERROR;
+ }
+ } else if (pid == tar) {
+ tar = -1;
+ if (!WIFEXITED(status) ||
+ WEXITSTATUS(status) != 0) {
+ fprintf(stderr,
+"Error from tar for data.tar.gz within '%s': %d\n",
+ debfile,
+ WEXITSTATUS(status));
+ result = RET_ERROR;
+ }
+ } else {
+ // WTH?
+ fprintf(stderr,
+"Who is %d, and why does this bother me?\n", pid);
+ }
+ }
+ }
+ if (RET_IS_OK(result))
+ return filelistcompressor_finish(&c, filelist);
+ else
+ filelistcompressor_cancel(&c);
+ return result;
+#endif
+}