/* dumpkconfig.c - read config data from kernel image Copyright (C) 2002 Andreas Ferber This program 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * This is a first attempt at extracting configuration data compiled in * via the kconfig patch (http://www.myipv6.de/patches/kconfig/) from a * kernel image without booting it. It only works if you enabled * CONFIG_PROC_CONFIG with gzip compression (CONFIG_PROC_CONFIG_GZ), * bzip2 or none compression are not yet supported (and probably will * never be). * * It will work on both a compressed (vmlinuz) or an uncompressed kernel * image (e.g. vmlinux or /proc/kcore), whatever you prefer. * * It needs zlib to extract the compressed data. Compile it with * * gcc -o dumpkconfig dumpkconfig.c -lz * * Command line interface is very simple at the moment: without * arguments it tries to read stdin, otherwise it tries to open the file * named by the first command line parameter. The only other parameter * recognized at the moment is "--help"/"-h", which prints a usage * message and exits. */ #include #include #include #include #include #include #include #include #include #include #include #include /* size of stream buffers */ #define BUF_SIZE 8192 /* stream data */ struct streambuf { int type; char *buf, *buf_base; size_t buf_size; size_t buf_avail; size_t pos; int err:1, eof:1, eof_pending:1; union { struct { FILE *fd; char *fname; } f; struct { z_streamp zstrm; struct streambuf *input; } z; } src; size_t (*read) (struct streambuf *s, void *buf, size_t size); void (*close) (struct streambuf *s); }; #define STREAM_FILE 1 #define STREAM_GZIP 2 /* gzip file header flags * unfortunately, zlib.h doesn't define these flags, so we have to * define it here */ #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ #define COMMENT 0x10 /* bit 4 set: file comment present */ #define RESERVED 0xE0 /* bits 5..7: reserved */ void error(const char *msg, ...) { va_list args; va_start(args, msg); vfprintf(stderr, msg, args); va_end(args); } #define FATAL(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) /* * Generic stream methods */ /* create a new empty stream object and allocate a buffer for it */ struct streambuf *alloc_stream(void) { struct streambuf *s; s = calloc(1, sizeof(struct streambuf)); if (s == NULL) FATAL("malloc"); s->buf_base = malloc(BUF_SIZE); if (s->buf_base == NULL) FATAL("malloc"); s->buf = s->buf_base; s->buf_size = BUF_SIZE; return s; } /* free a stream object and it's buffer */ void free_stream(struct streambuf *s) { free(s->buf_base); free(s); } /* fill the stream buffer via the ->read() function */ int fill_buffer(struct streambuf *s) { /* stop buffer fill if at least 80% are filled */ size_t min_fill = (s->buf_size/10)*8; if (s->err) return 0; if (s->buf != s->buf_base) { if (s->buf_avail != 0) memmove(s->buf_base, s->buf, s->buf_avail); s->buf = s->buf_base; } while (!s->eof_pending && !s->eof && s->buf_avail < min_fill) { size_t r = s->read(s, s->buf+s->buf_avail, s->buf_size-s->buf_avail); if (s->err) return 0; s->buf_avail += r; } if (s->eof_pending && s->buf_avail == 0) s->eof = 1; return 1; } void consume(struct streambuf *s, size_t bytes) { if (bytes < s->buf_avail) { s->buf += bytes; s->pos += bytes; s->buf_avail -= bytes; } else { s->pos += s->buf_avail; s->buf_avail = 0; } if (s->buf_avail == 0) { s->buf = s->buf_base; if (s->eof_pending) s->eof = 1; } } void close_stream(struct streambuf *s) { s->close(s); free_stream(s); } size_t file_stream_read(struct streambuf *s, void *buf, size_t size) { size_t r; assert(s->type == STREAM_FILE); r = fread(buf, 1, size, s->src.f.fd); if (ferror(s->src.f.fd)) FATAL(s->src.f.fname); if (feof(s->src.f.fd)) s->eof_pending = 1; return r; } void file_stream_close(struct streambuf *s) { if (s->src.f.fd) fclose(s->src.f.fd); if (s->src.f.fname) free(s->src.f.fname); } struct streambuf *file_stream_new(const char *fname) { struct streambuf *s; s = alloc_stream(); s->type = STREAM_FILE; s->read = file_stream_read; s->close = file_stream_close; if (fname == NULL) { s->src.f.fd = stdin; return s; } if ((s->src.f.fname = strdup(fname)) == NULL) FATAL("strdup"); if ((s->src.f.fd = fopen(fname, "r")) == NULL) FATAL(fname); return s; } voidpf zlib_alloc(voidpf opaque, uInt items, uInt size) { return malloc(items*size); } void zlib_free(voidpf opaque, voidpf mem) { free(mem); } size_t z_stream_read(struct streambuf *s, void *buf, size_t size) { z_streamp z = s->src.z.zstrm; struct streambuf *src = s->src.z.input; int zerr; assert(s->type == STREAM_GZIP); assert(!s->err); if (s->eof || s->eof_pending) return 0; if (!fill_buffer(src)) { s->err = 1; return 0; } z->next_in = src->buf; z->avail_in = src->buf_avail; z->next_out = buf; z->avail_out = size; zerr = inflate(z, Z_SYNC_FLUSH); if (zerr < 0) { s->err = 1; return 0; } if (zerr == Z_STREAM_END) s->eof_pending = 1; consume(src, src->buf_avail-z->avail_in); return size-z->avail_out; } void z_stream_close(struct streambuf *s) { assert(s->type == STREAM_GZIP); if (s->src.z.zstrm) { inflateEnd(s->src.z.zstrm); free(s->src.z.zstrm); s->src.z.zstrm = NULL; } } int skip_to_gzip_magic(struct streambuf *s) { static char magic[] = { 0x1f, 0x8b, 0x08 }; static int magic_len = sizeof(magic); while (1) { if (!fill_buffer(s)) return 0; while (s->buf_avail >= sizeof(magic)) { char *p = memchr(s->buf, magic[0], s->buf_avail-magic_len+1); if (!p) { consume(s, s->buf_avail-magic_len+1); break; } if (memcmp(p, magic, magic_len) == 0) { consume(s, p-s->buf); return 1; } consume(s, p-s->buf+1); } if (s->eof_pending) { consume(s, s->buf_avail); return 0; } } /* not reached */ } int skip_gzip_header(struct streambuf *s) { int flags; if (!fill_buffer(s)) return 0; flags = s->buf[3]; consume(s, 10); /* 10 == basic gzip header size */ if (flags & EXTRA_FIELD) { int extra = ((unsigned char)s->buf[1]<<8)+(unsigned char)s->buf[0]; consume(s, extra+2); } if (flags & ORIG_NAME) { char *p = memchr(s->buf, '\0', s->buf_avail); if (p) consume(s, p-s->buf); } if (flags & COMMENT) { char *p = memchr(s->buf, '\0', s->buf_avail); if (p) consume(s, p-s->buf); } if (flags & HEAD_CRC) consume(s, 2); return 1; } int try_zlib_init(struct streambuf *s) { struct streambuf *src = s->src.z.input; z_streamp z = s->src.z.zstrm; int zerr; if (!fill_buffer(src)) goto err_input; z->zalloc = zlib_alloc; z->zfree = zlib_free; z->next_in = src->buf; z->avail_in = src->buf_avail; z->next_out = s->buf; z->avail_out = s->buf_size; /* give negative window size to inflateInit2, because gzip files do * not contain zlib headers which are expected otherwise */ if ((zerr = inflateInit2(z, -MAX_WBITS)) != Z_OK) goto err_zinit; zerr = inflate(z, Z_SYNC_FLUSH); if (zerr < 0) goto err_decomp; s->buf_avail = s->buf_size-z->avail_out; if (zerr == Z_STREAM_END) s->eof_pending = 1; consume(src, src->buf_avail-z->avail_in); return 1; err_decomp: inflateEnd(z); err_zinit: consume(src, 1); err_input: return 0; } struct streambuf *z_stream_new(struct streambuf *src) { struct streambuf *s; z_streamp z; s = alloc_stream(); s->type = STREAM_GZIP; s->read = z_stream_read; s->close = z_stream_close; s->src.z.input = src; z = calloc(1, sizeof(z_stream)); if (z == NULL) FATAL("malloc"); s->src.z.zstrm = z; while (skip_to_gzip_magic(src)) { if (!skip_gzip_header(src)) break; if (try_zlib_init(s)) return s; /* we got the zlib stream up and running, cross fingers... */ if (src->err) break; } /* no gzip data to work on found (or source error...) */ free(z); free_stream(s); return NULL; } void z_stream_finish(struct streambuf *s) { assert(s->type == STREAM_GZIP); while (!s->eof && !s->err) { consume(s, s->buf_avail); fill_buffer(s); } consume(s, s->buf_avail); } void dump_stream(struct streambuf *s, int fd) { do { write(fd, s->buf, s->buf_avail); consume(s, s->buf_avail); fill_buffer(s); } while (!s->eof && !s->err); } int is_config(struct streambuf *s) { static char magic[] = "CONFIG_"; static int magic_len = sizeof(magic)-1; char *p; if ((p = memchr(s->buf, magic[0], s->buf_avail-magic_len)) != NULL) if (memcmp(p, magic, magic_len) == 0) return 1; return 0; } int dump_if_config(struct streambuf *s) { if (!fill_buffer(s)) return 0; if (is_config(s)) { dump_stream(s, STDOUT_FILENO); if (s->err) { error("Error dumping config data: %s\n", s->src.z.zstrm->msg); exit(EXIT_FAILURE); } return 1; } return 0; } int extract_config(const char *fname) { struct streambuf *f, *v, *c; int ret = 1; f = v = c = NULL; /* open outmost stream directly reading from file */ f = file_stream_new(fname); if (f == NULL) return 0; /* outer gzip encoding... */ while ((v = z_stream_new(f)) != NULL) { if (dump_if_config(v)) goto cleanup; /* inner gzip encoding... */ while ((c = z_stream_new(v)) != NULL) { if (dump_if_config(c)) goto cleanup; z_stream_finish(c); close_stream(c); } z_stream_finish(v); close_stream(v); } error("No config data found.\n" "Did you enable CONFIG_PROC_CONFIG and CONFIG_PROC_CONFIG_GZ?\n"); ret = 0; cleanup: if (c) close_stream(c); if (v) close_stream(v); if (f) close_stream(f); return ret; } void usage(const char *me) { fprintf(stderr, "Usage: %s [kernelimage]\n", me); exit(EXIT_SUCCESS); } int main(int argc, char *argv[]) { if (argc > 2) usage(argv[0]); if (argc == 2) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) usage(argv[0]); return !extract_config(argv[1]); } /* no filename, use stdin */ return !extract_config(NULL); } /* * vi:ts=4 sw=4 expandtab * vi:tags=./tags,./../tags,./../../tags,tags,../tags,../../tags */