aboutsummaryrefslogtreecommitdiffstats
path: root/tools/gpgtar-list.c
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2010-06-07 13:33:02 +0000
committerWerner Koch <[email protected]>2010-06-07 13:33:02 +0000
commitbbe388b5db35be6ffece8ebd42f11372af016763 (patch)
tree73e1fe9697b969be66bd89953125010e5721efe1 /tools/gpgtar-list.c
parentPrint --version etc via estream (diff)
downloadgnupg-bbe388b5db35be6ffece8ebd42f11372af016763.tar.gz
gnupg-bbe388b5db35be6ffece8ebd42f11372af016763.zip
Add unfinished gpgtar.
Collected changes and ports of bug fixes from stable.
Diffstat (limited to 'tools/gpgtar-list.c')
-rw-r--r--tools/gpgtar-list.c324
1 files changed, 324 insertions, 0 deletions
diff --git a/tools/gpgtar-list.c b/tools/gpgtar-list.c
new file mode 100644
index 000000000..82711c19e
--- /dev/null
+++ b/tools/gpgtar-list.c
@@ -0,0 +1,324 @@
+/* gpgtar-list.c - List a TAR archive
+ * Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "i18n.h"
+#include "gpgtar.h"
+
+
+
+static unsigned long long
+parse_xoctal (const void *data, size_t length, const char *filename)
+{
+ const unsigned char *p = data;
+ unsigned long long value;
+
+ if (!length)
+ value = 0;
+ else if ( (*p & 0x80))
+ {
+ /* Binary format. */
+ value = (*p++ & 0x7f);
+ while (--length)
+ {
+ value <<= 8;
+ value |= *p++;
+ }
+ }
+ else
+ {
+ /* Octal format */
+ value = 0;
+ /* Skip leading spaces and zeroes. */
+ for (; length && (*p == ' ' || *p == '0'); length--, p++)
+ ;
+ for (; length && *p; length--, p++)
+ {
+ if (*p >= '0' && *p <= '7')
+ {
+ value <<= 3;
+ value += (*p - '0');
+ }
+ else
+ {
+ log_error ("%s: invalid octal number encountered - assuming 0\n",
+ filename);
+ value = 0;
+ break;
+ }
+ }
+ }
+ return value;
+}
+
+
+static tar_header_t
+parse_header (const void *record, const char *filename)
+{
+ const struct ustar_raw_header *raw = record;
+ size_t n, namelen, prefixlen;
+ tar_header_t header;
+ int use_prefix;
+
+ use_prefix = (!memcmp (raw->magic, "ustar", 5)
+ && (raw->magic[5] == ' ' || !raw->magic[5]));
+
+
+ for (namelen=0; namelen < sizeof raw->name && raw->name[namelen]; namelen++)
+ ;
+ if (namelen == sizeof raw->name)
+ log_info ("%s: warning: name not terminated by a nul byte\n",
+ filename);
+ for (n=namelen+1; n < sizeof raw->name; n++)
+ if (raw->name[n])
+ {
+ log_info ("%s: warning: garbage after name\n", filename);
+ break;
+ }
+
+
+ if (use_prefix && raw->prefix[0])
+ {
+ for (prefixlen=0; (prefixlen < sizeof raw->prefix
+ && raw->prefix[prefixlen]); prefixlen++)
+ ;
+ if (prefixlen == sizeof raw->prefix)
+ log_info ("%s: warning: prefix not terminated by a nul byte\n",
+ filename);
+ for (n=prefixlen+1; n < sizeof raw->prefix; n++)
+ if (raw->prefix[n])
+ {
+ log_info ("%s: warning: garbage after prefix\n", filename);
+ break;
+ }
+ }
+ else
+ prefixlen = 0;
+
+ header = xtrycalloc (1, sizeof *header + prefixlen + 1 + namelen);
+ if (!header)
+ {
+ log_error ("%s: error allocating header: %s\n",
+ filename, gpg_strerror (gpg_error_from_syserror ()));
+ return NULL;
+ }
+ if (prefixlen)
+ {
+ n = prefixlen;
+ memcpy (header->name, raw->prefix, n);
+ if (raw->prefix[n-1] != '/')
+ header->name[n++] = '/';
+ }
+ else
+ n = 0;
+ memcpy (header->name+n, raw->name, namelen);
+ header->name[n+namelen] = 0;
+
+ header->mode = parse_xoctal (raw->mode, sizeof raw->mode, filename);
+ header->uid = parse_xoctal (raw->uid, sizeof raw->uid, filename);
+ header->gid = parse_xoctal (raw->gid, sizeof raw->gid, filename);
+ header->size = parse_xoctal (raw->size, sizeof raw->size, filename);
+ header->mtime = parse_xoctal (raw->mtime, sizeof raw->mtime, filename);
+ /* checksum = */
+ switch (raw->typeflag[0])
+ {
+ case '0': header->typeflag = TF_REGULAR; break;
+ case '1': header->typeflag = TF_HARDLINK; break;
+ case '2': header->typeflag = TF_SYMLINK; break;
+ case '3': header->typeflag = TF_CHARDEV; break;
+ case '4': header->typeflag = TF_BLOCKDEV; break;
+ case '5': header->typeflag = TF_DIRECTORY; break;
+ case '6': header->typeflag = TF_FIFO; break;
+ case '7': header->typeflag = TF_RESERVED; break;
+ default: header->typeflag = TF_UNKNOWN; break;
+ }
+
+
+ /* Compute the number of data records following this header. */
+ if (header->typeflag == TF_REGULAR || header->typeflag == TF_UNKNOWN)
+ header->nrecords = (header->size + RECORDSIZE-1)/RECORDSIZE;
+ else
+ header->nrecords = 0;
+
+
+ return header;
+}
+
+
+
+/* Read the next block, assming it is a tar header. Returns a header
+ object on success or NULL one error. In case of an error an error
+ message has been printed. */
+static tar_header_t
+read_header (estream_t stream)
+{
+ gpg_error_t err;
+ char record[RECORDSIZE];
+ int i;
+
+ err = read_record (stream, record);
+ if (err)
+ return NULL;
+
+ for (i=0; i < RECORDSIZE && !record[i]; i++)
+ ;
+ if (i == RECORDSIZE)
+ {
+ /* All zero header - check whether it is the first part of an
+ end of archive mark. */
+ err = read_record (stream, record);
+ if (err)
+ return NULL;
+
+ for (i=0; i < RECORDSIZE && !record[i]; i++)
+ ;
+ if (i != RECORDSIZE)
+ log_info ("%s: warning: skipping empty header\n",
+ es_fname_get (stream));
+ else
+ {
+ /* End of archive - FIXME: we might want to check for garbage. */
+ return NULL;
+ }
+ }
+
+ return parse_header (record, es_fname_get (stream));
+}
+
+
+/* Skip the data records according to HEADER. Prints an error message
+ on error and return -1. */
+static int
+skip_data (estream_t stream, tar_header_t header)
+{
+ char record[RECORDSIZE];
+ unsigned long long n;
+
+ for (n=0; n < header->nrecords; n++)
+ {
+ if (read_record (stream, record))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+static void
+print_header (tar_header_t header, estream_t out)
+{
+ unsigned long mask;
+ char modestr[10+1];
+ int i;
+
+ *modestr = '?';
+ switch (header->typeflag)
+ {
+ case TF_REGULAR: *modestr = '-'; break;
+ case TF_HARDLINK: *modestr = 'h'; break;
+ case TF_SYMLINK: *modestr = 'l'; break;
+ case TF_CHARDEV: *modestr = 'c'; break;
+ case TF_BLOCKDEV: *modestr = 'b'; break;
+ case TF_DIRECTORY:*modestr = 'd'; break;
+ case TF_FIFO: *modestr = 'f'; break;
+ case TF_RESERVED: *modestr = '='; break;
+ case TF_UNKNOWN: break;
+ case TF_NOTSUP: break;
+ }
+ for (mask = 0400, i = 0; i < 9; i++, mask >>= 1)
+ modestr[1+i] = (header->mode & mask)? "rwxrwxrwx"[i]:'-';
+ if ((header->typeflag & 04000))
+ modestr[3] = modestr[3] == 'x'? 's':'S';
+ if ((header->typeflag & 02000))
+ modestr[6] = modestr[6] == 'x'? 's':'S';
+ if ((header->typeflag & 01000))
+ modestr[9] = modestr[9] == 'x'? 't':'T';
+ modestr[10] = 0;
+
+ es_fprintf (out, "%s %lu %lu/%lu %12llu %s %s\n",
+ modestr, header->nlink, header->uid, header->gid, header->size,
+ isotimestamp (header->mtime), header->name);
+}
+
+
+
+/* List the tarball FILENAME or, if FILENAME is NULL, the tarball read
+ from stdin. */
+void
+gpgtar_list (const char *filename)
+{
+ gpg_error_t err;
+ estream_t stream;
+ tar_header_t header;
+
+ if (filename)
+ {
+ stream = es_fopen (filename, "rb");
+ if (!stream)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error opening `%s': %s\n", filename, gpg_strerror (err));
+ return;
+ }
+ }
+ else
+ stream = es_stdin; /* FIXME: How can we enforce binary mode? */
+
+ for (;;)
+ {
+ header = read_header (stream);
+ if (!header)
+ goto leave;
+
+ print_header (header, es_stdout);
+
+ if (skip_data (stream, header))
+ goto leave;
+ xfree (header);
+ header = NULL;
+ }
+
+
+ leave:
+ xfree (header);
+ if (filename)
+ es_fclose (stream);
+ return;
+}
+
+tar_header_t
+gpgtar_read_header (estream_t stream)
+{
+ /*FIXME: Change to return an error code. */
+ return read_header (stream);
+}
+
+void
+gpgtar_print_header (tar_header_t header, estream_t out)
+{
+ if (header && out)
+ print_header (header, out);
+}