1 /* 2 * Copyright (c) 2020 iXsystems, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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 24 * SUCH DAMAGE. 25 * 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/types.h> 32 #include <sys/kmem.h> 33 #include <sys/kmem_cache.h> 34 #include <sys/zmod.h> 35 #if __FreeBSD_version >= 1300041 36 #include <contrib/zlib/zlib.h> 37 #else 38 #include <sys/zlib.h> 39 #endif 40 #include <sys/kobj.h> 41 42 43 /*ARGSUSED*/ 44 static void * 45 zcalloc(void *opaque, uint_t items, uint_t size) 46 { 47 48 return (malloc((size_t)items*size, M_SOLARIS, M_NOWAIT)); 49 } 50 51 /*ARGSUSED*/ 52 static void 53 zcfree(void *opaque, void *ptr) 54 { 55 56 free(ptr, M_SOLARIS); 57 } 58 59 static int 60 zlib_deflateInit(z_stream *stream, int level) 61 { 62 63 stream->zalloc = zcalloc; 64 stream->opaque = NULL; 65 stream->zfree = zcfree; 66 67 return (deflateInit(stream, level)); 68 } 69 70 static int 71 zlib_deflate(z_stream *stream, int flush) 72 { 73 return (deflate(stream, flush)); 74 } 75 76 static int 77 zlib_deflateEnd(z_stream *stream) 78 { 79 return (deflateEnd(stream)); 80 } 81 82 static int 83 zlib_inflateInit(z_stream *stream) 84 { 85 stream->zalloc = zcalloc; 86 stream->opaque = NULL; 87 stream->zfree = zcfree; 88 89 return (inflateInit(stream)); 90 } 91 92 static int 93 zlib_inflate(z_stream *stream, int finish) 94 { 95 #if __FreeBSD_version >= 1300024 96 return (inflate(stream, finish)); 97 #else 98 return (_zlib104_inflate(stream, finish)); 99 #endif 100 } 101 102 103 static int 104 zlib_inflateEnd(z_stream *stream) 105 { 106 return (inflateEnd(stream)); 107 } 108 109 /* 110 * A kmem_cache is used for the zlib workspaces to avoid having to vmalloc 111 * and vfree for every call. Using a kmem_cache also has the advantage 112 * that improves the odds that the memory used will be local to this cpu. 113 * To further improve things it might be wise to create a dedicated per-cpu 114 * workspace for use. This would take some additional care because we then 115 * must disable preemption around the critical section, and verify that 116 * zlib_deflate* and zlib_inflate* never internally call schedule(). 117 */ 118 static void * 119 zlib_workspace_alloc(int flags) 120 { 121 // return (kmem_cache_alloc(zlib_workspace_cache, flags)); 122 return (NULL); 123 } 124 125 static void 126 zlib_workspace_free(void *workspace) 127 { 128 // kmem_cache_free(zlib_workspace_cache, workspace); 129 } 130 131 /* 132 * Compresses the source buffer into the destination buffer. The level 133 * parameter has the same meaning as in deflateInit. sourceLen is the byte 134 * length of the source buffer. Upon entry, destLen is the total size of the 135 * destination buffer, which must be at least 0.1% larger than sourceLen plus 136 * 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. 137 * 138 * compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough 139 * memory, Z_BUF_ERROR if there was not enough room in the output buffer, 140 * Z_STREAM_ERROR if the level parameter is invalid. 141 */ 142 int 143 z_compress_level(void *dest, size_t *destLen, const void *source, 144 size_t sourceLen, int level) 145 { 146 z_stream stream; 147 int err; 148 149 bzero(&stream, sizeof (stream)); 150 stream.next_in = (Byte *)source; 151 stream.avail_in = (uInt)sourceLen; 152 stream.next_out = dest; 153 stream.avail_out = (uInt)*destLen; 154 stream.opaque = NULL; 155 156 if ((size_t)stream.avail_out != *destLen) 157 return (Z_BUF_ERROR); 158 159 stream.opaque = zlib_workspace_alloc(KM_SLEEP); 160 #if 0 161 if (!stream.opaque) 162 return (Z_MEM_ERROR); 163 #endif 164 err = zlib_deflateInit(&stream, level); 165 if (err != Z_OK) { 166 zlib_workspace_free(stream.opaque); 167 return (err); 168 } 169 170 err = zlib_deflate(&stream, Z_FINISH); 171 if (err != Z_STREAM_END) { 172 zlib_deflateEnd(&stream); 173 zlib_workspace_free(stream.opaque); 174 return (err == Z_OK ? Z_BUF_ERROR : err); 175 } 176 *destLen = stream.total_out; 177 178 err = zlib_deflateEnd(&stream); 179 zlib_workspace_free(stream.opaque); 180 return (err); 181 } 182 183 /* 184 * Decompresses the source buffer into the destination buffer. sourceLen is 185 * the byte length of the source buffer. Upon entry, destLen is the total 186 * size of the destination buffer, which must be large enough to hold the 187 * entire uncompressed data. (The size of the uncompressed data must have 188 * been saved previously by the compressor and transmitted to the decompressor 189 * by some mechanism outside the scope of this compression library.) 190 * Upon exit, destLen is the actual size of the compressed buffer. 191 * This function can be used to decompress a whole file at once if the 192 * input file is mmap'ed. 193 * 194 * uncompress returns Z_OK if success, Z_MEM_ERROR if there was not 195 * enough memory, Z_BUF_ERROR if there was not enough room in the output 196 * buffer, or Z_DATA_ERROR if the input data was corrupted. 197 */ 198 int 199 z_uncompress(void *dest, size_t *destLen, const void *source, size_t sourceLen) 200 { 201 z_stream stream; 202 int err; 203 204 bzero(&stream, sizeof (stream)); 205 206 stream.next_in = (Byte *)source; 207 stream.avail_in = (uInt)sourceLen; 208 stream.next_out = dest; 209 stream.avail_out = (uInt)*destLen; 210 211 if ((size_t)stream.avail_out != *destLen) 212 return (Z_BUF_ERROR); 213 214 stream.opaque = zlib_workspace_alloc(KM_SLEEP); 215 #if 0 216 if (!stream.opaque) 217 return (Z_MEM_ERROR); 218 #endif 219 err = zlib_inflateInit(&stream); 220 if (err != Z_OK) { 221 zlib_workspace_free(stream.opaque); 222 return (err); 223 } 224 225 err = zlib_inflate(&stream, Z_FINISH); 226 if (err != Z_STREAM_END) { 227 zlib_inflateEnd(&stream); 228 zlib_workspace_free(stream.opaque); 229 230 if (err == Z_NEED_DICT || 231 (err == Z_BUF_ERROR && stream.avail_in == 0)) 232 return (Z_DATA_ERROR); 233 234 return (err); 235 } 236 *destLen = stream.total_out; 237 238 err = zlib_inflateEnd(&stream); 239 zlib_workspace_free(stream.opaque); 240 241 return (err); 242 } 243