diff options
author | Werner Koch <[email protected]> | 2010-06-07 13:33:02 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2010-06-07 13:33:02 +0000 |
commit | bbe388b5db35be6ffece8ebd42f11372af016763 (patch) | |
tree | 73e1fe9697b969be66bd89953125010e5721efe1 /tools/gpgtar-list.c | |
parent | Print --version etc via estream (diff) | |
download | gnupg-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.c | 324 |
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); +} |