1 /*
2  * Original version by Jean-loup Gailly. Modified for use by the
3  * Erlang run-time system and efile_driver; names of all external
4  * functions changed to avoid conflicts with the official gzio.c file.
5  *
6  * gzio.c -- IO on .gz files
7  * Copyright (C) 1995-1996 Jean-loup Gailly.
8  * For conditions of distribution and use, see copyright notice in zlib.h
9  */
10 /* %ExternalCopyright% */
11 
12 #ifdef HAVE_CONFIG_H
13 #  include "config.h"
14 #endif
15 #include <stdio.h>
16 #include <string.h> /* ssize_t on Mac OS X */
17 #include <errno.h>
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21 #include <ctype.h>
22 
23 #include "erl_driver.h"
24 #include "sys.h"
25 
26 #include "gzio_zutil.h"
27 #include "erl_zlib.h"
28 #include "gzio.h"
29 
30 static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
31 
32 /* ===========================================================================
33    Uncompresses the buffer given and returns a pointer to a binary.
34    If the buffer was not compressed with gzip, the buffer contents
35    will be copied unchanged into the binary.
36 
37    If a `gzip' header was found, but there were subsequent errors,
38    a NULL pointer is returned.
39 */
40 
41 ErlDrvBinary*
erts_gzinflate_buffer(char * start,uLong size)42 erts_gzinflate_buffer(char* start, uLong size)
43 {
44     ErlDrvBinary* bin;
45     ErlDrvBinary* bin2;
46     z_stream zstr;
47     unsigned char* bptr;
48 
49     /*
50      * Check for the magic bytes beginning a GZIP header.
51      */
52     bptr = (unsigned char *) start;
53     if (size < 2 || bptr[0] != gz_magic[0] || bptr[1] != gz_magic[1]) {
54 	/* No GZIP header -- just copy the data into a new binary */
55 	if ((bin = driver_alloc_binary(size)) == NULL) {
56 	    return NULL;
57 	}
58 	memcpy(bin->orig_bytes, start, size);
59 	return bin;
60     }
61 
62     /*
63      * The magic bytes for a GZIP header are there. Now try to decompress.
64      * It is an error if the GZIP header is not correct.
65      */
66 
67     zstr.next_in = (unsigned char*) start;
68     zstr.avail_in = size;
69     erl_zlib_alloc_init(&zstr);
70     size *= 2;
71     if ((bin = driver_alloc_binary(size)) == NULL) {
72 	return NULL;
73     }
74     if (inflateInit2(&zstr, 15+16) != Z_OK) { /* Decode GZIP format */
75 	driver_free(bin);
76 	return NULL;
77     }
78     for (;;) {
79 	int status;
80 
81 	zstr.next_out = (unsigned char *) bin->orig_bytes + zstr.total_out;
82 	zstr.avail_out = size - zstr.total_out;
83 	status = inflate(&zstr, Z_NO_FLUSH);
84 	if (status == Z_OK) {
85 	    size *= 2;
86 	    if ((bin2 = driver_realloc_binary(bin, size)) == NULL) {
87 	    error:
88 		driver_free_binary(bin);
89 		inflateEnd(&zstr);
90 		return NULL;
91 	    }
92 	    bin = bin2;
93 	} else if (status == Z_STREAM_END) {
94 	    if ((bin2 = driver_realloc_binary(bin, zstr.total_out)) == NULL) {
95 		goto error;
96 	    }
97 	    inflateEnd(&zstr);
98 	    return bin2;
99 	} else {
100 	    goto error;
101 	}
102     }
103 }
104 
105 /* ===========================================================================
106    Compresses the buffer given and returns a pointer to a binary.
107    A NULL pointer is returned if any error occurs.
108    Writes a gzip header as well.
109 */
110 
111 #define GZIP_HD_SIZE 10
112 #define GZIP_TL_SIZE 8
113 
114 #define GZIP_X_SIZE (GZIP_HD_SIZE+GZIP_TL_SIZE)
115 
116 ErlDrvBinary*
erts_gzdeflate_buffer(char * start,uLong size)117 erts_gzdeflate_buffer(char* start, uLong size)
118 {
119     z_stream c_stream; /* compression stream */
120     ErlDrvBinary* bin;
121     ErlDrvBinary* bin2;
122     uLong    crc;     /* crc32 of uncompressed data */
123     uLong    szIn;
124     Byte* ptr;
125     int comprLen = size + (size/1000) + 1 + 12; /* see zlib.h */
126 
127     crc = crc32(0L, Z_NULL, 0);
128     erl_zlib_alloc_init(&c_stream);
129 
130     if (deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION,
131 		     Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, 0) != Z_OK)
132 	return NULL;
133 
134     if ((bin = driver_alloc_binary(comprLen+GZIP_X_SIZE)) == NULL)
135 	return NULL;
136     sprintf(bin->orig_bytes, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
137 	    Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
138 
139     c_stream.next_out = ((Byte*) bin->orig_bytes)+GZIP_HD_SIZE;
140     c_stream.avail_out = (uInt) bin->orig_size - GZIP_HD_SIZE;
141     c_stream.next_in  = (Byte*) start;
142     c_stream.avail_in = (uInt) size;
143 
144     if (deflate(&c_stream, Z_FINISH) != Z_STREAM_END) {
145 	driver_free_binary(bin);
146 	return NULL;
147     }
148     crc = crc32(crc, (unsigned char*)start, size);
149     ptr = c_stream.next_out;
150     szIn = c_stream.total_in;
151 
152     *ptr++ = (crc & 0xff); crc >>= 8;
153     *ptr++ = (crc & 0xff); crc >>= 8;
154     *ptr++ = (crc & 0xff); crc >>= 8;
155     *ptr++ = (crc & 0xff); crc >>= 8;
156 
157     *ptr++ = (szIn & 0xff); szIn >>= 8;
158     *ptr++ = (szIn & 0xff); szIn >>= 8;
159     *ptr++ = (szIn & 0xff); szIn >>= 8;
160     *ptr++ = (szIn & 0xff); szIn >>= 8;
161 
162     if (deflateEnd(&c_stream) != Z_OK) {
163 	driver_free_binary(bin);
164 	return NULL;
165     }
166     size = ptr - (Byte*)bin->orig_bytes;
167 
168     if ((bin2 = driver_realloc_binary(bin, size)) == NULL)
169 	driver_free_binary(bin);
170     return bin2;
171 }
172 
173