2 * Copyright (c) 2014 Mark Johnston <markj@FreeBSD.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in
11 * the documentation and/or other materials provided with the
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/zutil.h>
37 #define KERN_GZ_HDRLEN 10 /* gzip header length */
38 #define KERN_GZ_TRAILERLEN 8 /* gzip trailer length */
39 #define KERN_GZ_MAGIC1 0x1f /* first magic byte */
40 #define KERN_GZ_MAGIC2 0x8b /* second magic byte */
42 MALLOC_DEFINE(M_GZIO
, "gzio", "zlib state");
45 uint8_t * gz_buffer
; /* output buffer */
46 size_t gz_bufsz
; /* total buffer size */
47 off_t gz_off
; /* offset into the output stream */
48 enum gzio_mode gz_mode
; /* stream mode */
49 uint32_t gz_crc
; /* stream CRC32 */
50 gzio_cb gz_cb
; /* output callback */
51 void * gz_arg
; /* private callback arg */
52 z_stream gz_stream
; /* zlib state */
55 static void * gz_alloc(void *, u_int
, u_int
);
56 static void gz_free(void *, void *);
57 static int gz_write(struct gzio_stream
*, void *, u_int
, int);
60 gzio_init(gzio_cb cb
, enum gzio_mode mode
, size_t bufsz
, int level
, void *arg
)
62 struct gzio_stream
*s
;
66 if (bufsz
< KERN_GZ_HDRLEN
)
68 if (mode
!= GZIO_DEFLATE
)
71 s
= gz_alloc(NULL
, 1, sizeof(*s
));
73 s
->gz_buffer
= gz_alloc(NULL
, 1, s
->gz_bufsz
);
79 s
->gz_stream
.zalloc
= gz_alloc
;
80 s
->gz_stream
.zfree
= gz_free
;
81 s
->gz_stream
.opaque
= NULL
;
82 s
->gz_stream
.next_in
= Z_NULL
;
83 s
->gz_stream
.avail_in
= 0;
85 error
= deflateInit2(&s
->gz_stream
, level
, Z_DEFLATED
, -MAX_WBITS
,
86 DEF_MEM_LEVEL
, Z_DEFAULT_STRATEGY
);
90 s
->gz_stream
.avail_out
= s
->gz_bufsz
;
91 s
->gz_stream
.next_out
= s
->gz_buffer
;
93 /* Write the gzip header to the output buffer. */
95 memset(hdr
, 0, KERN_GZ_HDRLEN
);
96 hdr
[0] = KERN_GZ_MAGIC1
;
97 hdr
[1] = KERN_GZ_MAGIC2
;
100 s
->gz_stream
.next_out
+= KERN_GZ_HDRLEN
;
101 s
->gz_stream
.avail_out
-= KERN_GZ_HDRLEN
;
106 gz_free(NULL
, s
->gz_buffer
);
112 gzio_write(struct gzio_stream
*s
, void *data
, u_int len
)
115 return (gz_write(s
, data
, len
, Z_NO_FLUSH
));
119 gzio_flush(struct gzio_stream
*s
)
122 return (gz_write(s
, NULL
, 0, Z_FINISH
));
126 gzio_fini(struct gzio_stream
*s
)
129 (void)deflateEnd(&s
->gz_stream
);
130 gz_free(NULL
, s
->gz_buffer
);
135 gz_alloc(void *arg __unused
, u_int n
, u_int sz
)
139 * Memory for zlib state is allocated using M_NODUMP since it may be
140 * used to compress a kernel dump, and we don't want zlib to attempt to
141 * compress its own state.
143 return (malloc(n
* sz
, M_GZIO
, M_WAITOK
| M_ZERO
| M_NODUMP
));
147 gz_free(void *arg __unused
, void *ptr
)
154 gz_write(struct gzio_stream
*s
, void *buf
, u_int len
, int zflag
)
156 uint8_t trailer
[KERN_GZ_TRAILERLEN
];
160 KASSERT(zflag
== Z_FINISH
|| zflag
== Z_NO_FLUSH
,
161 ("unexpected flag %d", zflag
));
162 KASSERT(s
->gz_mode
== GZIO_DEFLATE
,
163 ("invalid stream mode %d", s
->gz_mode
));
166 s
->gz_stream
.avail_in
= len
;
167 s
->gz_stream
.next_in
= buf
;
168 s
->gz_crc
= crc32_raw(buf
, len
, s
->gz_crc
);
174 zerror
= deflate(&s
->gz_stream
, zflag
);
175 if (zerror
!= Z_OK
&& zerror
!= Z_STREAM_END
) {
180 if (s
->gz_stream
.avail_out
== 0 || zerror
== Z_STREAM_END
) {
182 * Our output buffer is full or there's nothing left
183 * to produce, so we're flushing the buffer.
185 len
= s
->gz_bufsz
- s
->gz_stream
.avail_out
;
186 if (zerror
== Z_STREAM_END
) {
188 * Try to pack as much of the trailer into the
189 * output buffer as we can.
191 ((uint32_t *)trailer
)[0] = s
->gz_crc
;
192 ((uint32_t *)trailer
)[1] =
193 s
->gz_stream
.total_in
;
194 room
= MIN(KERN_GZ_TRAILERLEN
,
196 memcpy(s
->gz_buffer
+ len
, trailer
, room
);
200 error
= s
->gz_cb(s
->gz_buffer
, len
, s
->gz_off
,
206 s
->gz_stream
.next_out
= s
->gz_buffer
;
207 s
->gz_stream
.avail_out
= s
->gz_bufsz
;
210 * If we couldn't pack the trailer into the output
211 * buffer, write it out now.
213 if (zerror
== Z_STREAM_END
&& room
< KERN_GZ_TRAILERLEN
)
214 error
= s
->gz_cb(trailer
+ room
,
215 KERN_GZ_TRAILERLEN
- room
, s
->gz_off
,
218 } while (zerror
!= Z_STREAM_END
&&
219 (zflag
== Z_FINISH
|| s
->gz_stream
.avail_in
> 0));