Branch data Line data Source code
1 : : /* GIO - GLib Input, Output and Streaming Library
2 : : *
3 : : * Copyright (C) 2009 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: Alexander Larsson <alexl@redhat.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include "gzlibcompressor.h"
26 : :
27 : : #include <errno.h>
28 : : #include <zlib.h>
29 : : #include <string.h>
30 : :
31 : : #include "gfileinfo.h"
32 : : #include "gioerror.h"
33 : : #include "gioenums.h"
34 : : #include "gioenumtypes.h"
35 : : #include "glibintl.h"
36 : :
37 : :
38 : : enum {
39 : : PROP_0,
40 : : PROP_FORMAT,
41 : : PROP_LEVEL,
42 : : PROP_FILE_INFO,
43 : : PROP_OS
44 : : };
45 : :
46 : : /**
47 : : * GZlibCompressor:
48 : : *
49 : : * `GZlibCompressor` is an implementation of [iface@Gio.Converter] that
50 : : * compresses data using zlib.
51 : : */
52 : :
53 : : static void g_zlib_compressor_iface_init (GConverterIface *iface);
54 : :
55 : : struct _GZlibCompressor
56 : : {
57 : : GObject parent_instance;
58 : :
59 : : GZlibCompressorFormat format;
60 : : int level;
61 : : z_stream zstream;
62 : : gz_header gzheader;
63 : : GFileInfo *file_info;
64 : : int os;
65 : : };
66 : :
67 : : static void
68 : 38 : g_zlib_compressor_set_gzheader (GZlibCompressor *compressor)
69 : : {
70 : : /* On win32, these functions were not exported before 1.2.4 */
71 : : #if !defined (G_OS_WIN32) || ZLIB_VERNUM >= 0x1240
72 : : const gchar *filename;
73 : :
74 : 38 : if (compressor->format != G_ZLIB_COMPRESSOR_FORMAT_GZIP ||
75 : 20 : (compressor->file_info == NULL &&
76 : 16 : compressor->os < 0))
77 : 32 : return;
78 : :
79 : 6 : memset (&compressor->gzheader, 0, sizeof (gz_header));
80 : :
81 : 6 : if (compressor->os >= 0)
82 : 2 : compressor->gzheader.os = compressor->os;
83 : : else
84 : 4 : compressor->gzheader.os = 0x03; /* Unix */
85 : :
86 : 6 : if (compressor->file_info)
87 : : {
88 : 4 : filename = g_file_info_get_name (compressor->file_info);
89 : 4 : compressor->gzheader.name = (Bytef *) filename;
90 : 4 : compressor->gzheader.name_max = filename ? strlen (filename) + 1 : 0;
91 : :
92 : 4 : compressor->gzheader.time =
93 : 4 : (uLong) g_file_info_get_attribute_uint64 (compressor->file_info,
94 : : G_FILE_ATTRIBUTE_TIME_MODIFIED);
95 : : }
96 : :
97 : 6 : if (deflateSetHeader (&compressor->zstream, &compressor->gzheader) != Z_OK)
98 : 0 : g_warning ("unexpected zlib error: %s", compressor->zstream.msg);
99 : : #endif /* !G_OS_WIN32 || ZLIB >= 1.2.4 */
100 : : }
101 : :
102 : 4631 : G_DEFINE_TYPE_WITH_CODE (GZlibCompressor, g_zlib_compressor, G_TYPE_OBJECT,
103 : : G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
104 : : g_zlib_compressor_iface_init))
105 : :
106 : : static void
107 : 22 : g_zlib_compressor_finalize (GObject *object)
108 : : {
109 : : GZlibCompressor *compressor;
110 : :
111 : 22 : compressor = G_ZLIB_COMPRESSOR (object);
112 : :
113 : 22 : deflateEnd (&compressor->zstream);
114 : :
115 : 22 : if (compressor->file_info)
116 : 6 : g_object_unref (compressor->file_info);
117 : :
118 : 22 : G_OBJECT_CLASS (g_zlib_compressor_parent_class)->finalize (object);
119 : 22 : }
120 : :
121 : :
122 : : static void
123 : 50 : g_zlib_compressor_set_property (GObject *object,
124 : : guint prop_id,
125 : : const GValue *value,
126 : : GParamSpec *pspec)
127 : : {
128 : : GZlibCompressor *compressor;
129 : :
130 : 50 : compressor = G_ZLIB_COMPRESSOR (object);
131 : :
132 : 50 : switch (prop_id)
133 : : {
134 : 22 : case PROP_FORMAT:
135 : 22 : compressor->format = g_value_get_enum (value);
136 : 22 : break;
137 : :
138 : 22 : case PROP_LEVEL:
139 : 22 : compressor->level = g_value_get_int (value);
140 : 22 : break;
141 : :
142 : 6 : case PROP_FILE_INFO:
143 : 6 : g_zlib_compressor_set_file_info (compressor, g_value_get_object (value));
144 : 6 : break;
145 : :
146 : 0 : case PROP_OS:
147 : 0 : g_zlib_compressor_set_os (compressor, g_value_get_int (value));
148 : 0 : break;
149 : :
150 : 0 : default:
151 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
152 : 0 : break;
153 : : }
154 : :
155 : 50 : }
156 : :
157 : : static void
158 : 17 : g_zlib_compressor_get_property (GObject *object,
159 : : guint prop_id,
160 : : GValue *value,
161 : : GParamSpec *pspec)
162 : : {
163 : : GZlibCompressor *compressor;
164 : :
165 : 17 : compressor = G_ZLIB_COMPRESSOR (object);
166 : :
167 : 17 : switch (prop_id)
168 : : {
169 : 7 : case PROP_FORMAT:
170 : 7 : g_value_set_enum (value, compressor->format);
171 : 7 : break;
172 : :
173 : 7 : case PROP_LEVEL:
174 : 7 : g_value_set_int (value, compressor->level);
175 : 7 : break;
176 : :
177 : 1 : case PROP_FILE_INFO:
178 : 1 : g_value_set_object (value, compressor->file_info);
179 : 1 : break;
180 : :
181 : 2 : case PROP_OS:
182 : 2 : g_value_set_int (value, compressor->os);
183 : 2 : break;
184 : :
185 : 0 : default:
186 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
187 : 0 : break;
188 : : }
189 : 17 : }
190 : :
191 : : static void
192 : 22 : g_zlib_compressor_init (GZlibCompressor *compressor)
193 : : {
194 : 22 : compressor->os = -1;
195 : 22 : }
196 : :
197 : : static void
198 : 22 : g_zlib_compressor_constructed (GObject *object)
199 : : {
200 : : GZlibCompressor *compressor;
201 : : int res;
202 : :
203 : 22 : compressor = G_ZLIB_COMPRESSOR (object);
204 : :
205 : 22 : if (compressor->format == G_ZLIB_COMPRESSOR_FORMAT_GZIP)
206 : : {
207 : : /* + 16 for gzip */
208 : 12 : res = deflateInit2 (&compressor->zstream,
209 : : compressor->level, Z_DEFLATED,
210 : : MAX_WBITS + 16, 8,
211 : : Z_DEFAULT_STRATEGY);
212 : : }
213 : 10 : else if (compressor->format == G_ZLIB_COMPRESSOR_FORMAT_RAW)
214 : : {
215 : : /* negative wbits for raw */
216 : 3 : res = deflateInit2 (&compressor->zstream,
217 : : compressor->level, Z_DEFLATED,
218 : : -MAX_WBITS, 8,
219 : : Z_DEFAULT_STRATEGY);
220 : : }
221 : : else /* ZLIB */
222 : 7 : res = deflateInit (&compressor->zstream, compressor->level);
223 : :
224 : 22 : if (res == Z_MEM_ERROR )
225 : 0 : g_error ("GZlibCompressor: Not enough memory for zlib use");
226 : :
227 : 22 : if (res != Z_OK)
228 : 0 : g_warning ("unexpected zlib error: %s", compressor->zstream.msg);
229 : :
230 : 22 : g_zlib_compressor_set_gzheader (compressor);
231 : 22 : }
232 : :
233 : : static void
234 : 7 : g_zlib_compressor_class_init (GZlibCompressorClass *klass)
235 : : {
236 : 7 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
237 : :
238 : 7 : gobject_class->finalize = g_zlib_compressor_finalize;
239 : 7 : gobject_class->constructed = g_zlib_compressor_constructed;
240 : 7 : gobject_class->get_property = g_zlib_compressor_get_property;
241 : 7 : gobject_class->set_property = g_zlib_compressor_set_property;
242 : :
243 : : /**
244 : : * GZlibCompressor:format:
245 : : *
246 : : * The format of the compressed data.
247 : : *
248 : : * Since: 2.24
249 : : */
250 : 7 : g_object_class_install_property (gobject_class,
251 : : PROP_FORMAT,
252 : : g_param_spec_enum ("format", NULL, NULL,
253 : : G_TYPE_ZLIB_COMPRESSOR_FORMAT,
254 : : G_ZLIB_COMPRESSOR_FORMAT_ZLIB,
255 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
256 : : G_PARAM_STATIC_STRINGS));
257 : :
258 : : /**
259 : : * GZlibCompressor:level:
260 : : *
261 : : * The level of compression from `0` (no compression) to `9` (most
262 : : * compression).
263 : : *
264 : : * `-1` for the default level.
265 : : *
266 : : * Since: 2.24
267 : : */
268 : 7 : g_object_class_install_property (gobject_class,
269 : : PROP_LEVEL,
270 : : g_param_spec_int ("level", NULL, NULL,
271 : : -1, 9,
272 : : -1,
273 : : G_PARAM_READWRITE |
274 : : G_PARAM_CONSTRUCT_ONLY |
275 : : G_PARAM_STATIC_STRINGS));
276 : :
277 : : /**
278 : : * GZlibCompressor:file-info:
279 : : *
280 : : * A [class@Gio.FileInfo] containing file information to put into the gzip
281 : : * header.
282 : : *
283 : : * The file name and modification time from the file info will be used.
284 : : *
285 : : * This will only be used if non-`NULL` and
286 : : * [property@Gio.ZlibCompressor:format] is
287 : : * [enum@Gio.ZlibCompressorFormat.GZIP].
288 : : *
289 : : * Since: 2.26
290 : : */
291 : 7 : g_object_class_install_property (gobject_class,
292 : : PROP_FILE_INFO,
293 : : g_param_spec_object ("file-info", NULL, NULL,
294 : : G_TYPE_FILE_INFO,
295 : : G_PARAM_READWRITE |
296 : : G_PARAM_STATIC_STRINGS));
297 : :
298 : : /**
299 : : * GZlibCompressor:os:
300 : : *
301 : : * The OS code of the gzip header.
302 : : *
303 : : * This will be used if set to a non-negative value, and if
304 : : * [property@Gio.ZlibCompressor:format] is
305 : : * [enum@Gio.ZlibCompressorFormat.GZIP], the compressor will set the OS code of
306 : : * the gzip header to this value.
307 : : *
308 : : * If the value is unset, zlib will set the OS code depending on the platform.
309 : : * This may be undesirable when reproducible output is desired. In that case setting
310 : : * the OS code to `3` (for Unix) is recommended.
311 : : *
312 : : * Since: 2.86
313 : : */
314 : 7 : g_object_class_install_property (gobject_class,
315 : : PROP_OS,
316 : : g_param_spec_int ("os", NULL, NULL,
317 : : -1, 255,
318 : : -1,
319 : : G_PARAM_READWRITE |
320 : : G_PARAM_STATIC_STRINGS |
321 : : G_PARAM_EXPLICIT_NOTIFY));
322 : 7 : }
323 : :
324 : : /**
325 : : * g_zlib_compressor_new:
326 : : * @format: the format to use for the compressed data
327 : : * @level: compression level (`0`-`9`), `-1` for default
328 : : *
329 : : * Creates a compressor.
330 : : *
331 : : * Returns: a new [class@Gio.ZlibCompressor]
332 : : * Since: 2.24
333 : : **/
334 : : GZlibCompressor *
335 : 21 : g_zlib_compressor_new (GZlibCompressorFormat format,
336 : : int level)
337 : : {
338 : : GZlibCompressor *compressor;
339 : :
340 : 21 : compressor = g_object_new (G_TYPE_ZLIB_COMPRESSOR,
341 : : "format", format,
342 : : "level", level,
343 : : NULL);
344 : :
345 : 21 : return compressor;
346 : : }
347 : :
348 : : /**
349 : : * g_zlib_compressor_get_file_info:
350 : : * @compressor: a compressor
351 : : *
352 : : * Gets the [property@Gio.ZlibCompressor:file-info] property.
353 : : *
354 : : * Returns: (nullable) (transfer none): file info for the gzip header, if set
355 : : * Since: 2.26
356 : : */
357 : : GFileInfo *
358 : 6 : g_zlib_compressor_get_file_info (GZlibCompressor *compressor)
359 : : {
360 : 6 : g_return_val_if_fail (G_IS_ZLIB_COMPRESSOR (compressor), NULL);
361 : :
362 : 6 : return compressor->file_info;
363 : : }
364 : :
365 : : /**
366 : : * g_zlib_compressor_set_file_info:
367 : : * @compressor: a compressor
368 : : * @file_info: (nullable): file info for the gzip header
369 : : *
370 : : * Sets the [property@Gio.ZlibCompressor:file-info] property.
371 : : *
372 : : * Note: it is an error to call this function while a compression is in
373 : : * progress; it may only be called immediately after creation of @compressor,
374 : : * or after resetting it with [method@Gio.Converter.reset].
375 : : *
376 : : * Since: 2.26
377 : : */
378 : : void
379 : 6 : g_zlib_compressor_set_file_info (GZlibCompressor *compressor,
380 : : GFileInfo *file_info)
381 : : {
382 : 6 : g_return_if_fail (G_IS_ZLIB_COMPRESSOR (compressor));
383 : :
384 : 6 : if (file_info == compressor->file_info)
385 : 0 : return;
386 : :
387 : 6 : if (compressor->file_info)
388 : 0 : g_object_unref (compressor->file_info);
389 : 6 : if (file_info)
390 : 6 : g_object_ref (file_info);
391 : 6 : compressor->file_info = file_info;
392 : 6 : g_object_notify (G_OBJECT (compressor), "file-info");
393 : :
394 : 6 : g_zlib_compressor_set_gzheader (compressor);
395 : : }
396 : :
397 : : /**
398 : : * g_zlib_compressor_get_os:
399 : : * @compressor: a compressor
400 : : *
401 : : * Gets the [property@Gio.ZlibCompressor:os] property.
402 : : *
403 : : * Returns: the previously set OS value, or `-1` if unset
404 : : * Since: 2.86
405 : : */
406 : : int
407 : 1 : g_zlib_compressor_get_os (GZlibCompressor *compressor)
408 : : {
409 : 1 : g_return_val_if_fail (G_IS_ZLIB_COMPRESSOR (compressor), -1);
410 : :
411 : 1 : return compressor->os;
412 : : }
413 : :
414 : : /**
415 : : * g_zlib_compressor_set_os:
416 : : * @compressor: a compressor
417 : : * @os: the OS code to use, or `-1` to unset
418 : : *
419 : : * Sets the [property@Gio.ZlibCompressor:os] property.
420 : : *
421 : : * Note: it is an error to call this function while a compression is in
422 : : * progress; it may only be called immediately after creation of @compressor,
423 : : * or after resetting it with [method@Gio.Converter.reset].
424 : : *
425 : : * Since: 2.86
426 : : */
427 : : void
428 : 1 : g_zlib_compressor_set_os (GZlibCompressor *compressor,
429 : : int os)
430 : : {
431 : 1 : g_return_if_fail (G_IS_ZLIB_COMPRESSOR (compressor));
432 : 1 : g_return_if_fail (os >= -1 && os < 256);
433 : :
434 : 1 : if (os == compressor->os)
435 : 0 : return;
436 : :
437 : 1 : compressor->os = os;
438 : :
439 : 1 : g_object_notify (G_OBJECT (compressor), "os");
440 : :
441 : 1 : g_zlib_compressor_set_gzheader (compressor);
442 : : }
443 : :
444 : : static void
445 : 9 : g_zlib_compressor_reset (GConverter *converter)
446 : : {
447 : 9 : GZlibCompressor *compressor = G_ZLIB_COMPRESSOR (converter);
448 : : int res;
449 : :
450 : 9 : res = deflateReset (&compressor->zstream);
451 : 9 : if (res != Z_OK)
452 : 0 : g_warning ("unexpected zlib error: %s", compressor->zstream.msg);
453 : :
454 : : /* deflateReset reset the header too, so re-set it */
455 : 9 : g_zlib_compressor_set_gzheader (compressor);
456 : 9 : }
457 : :
458 : : static GConverterResult
459 : 4454 : g_zlib_compressor_convert (GConverter *converter,
460 : : const void *inbuf,
461 : : gsize inbuf_size,
462 : : void *outbuf,
463 : : gsize outbuf_size,
464 : : GConverterFlags flags,
465 : : gsize *bytes_read,
466 : : gsize *bytes_written,
467 : : GError **error)
468 : : {
469 : : GZlibCompressor *compressor;
470 : : int res;
471 : : int flush;
472 : :
473 : 4454 : compressor = G_ZLIB_COMPRESSOR (converter);
474 : :
475 : 4454 : compressor->zstream.next_in = (void *)inbuf;
476 : 4454 : compressor->zstream.avail_in = inbuf_size;
477 : :
478 : 4454 : compressor->zstream.next_out = outbuf;
479 : 4454 : compressor->zstream.avail_out = outbuf_size;
480 : :
481 : 4454 : flush = Z_NO_FLUSH;
482 : 4454 : if (flags & G_CONVERTER_INPUT_AT_END)
483 : 22 : flush = Z_FINISH;
484 : 4432 : else if (flags & G_CONVERTER_FLUSH)
485 : 8 : flush = Z_SYNC_FLUSH;
486 : :
487 : 4454 : res = deflate (&compressor->zstream, flush);
488 : :
489 : 4454 : if (res == Z_MEM_ERROR)
490 : : {
491 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
492 : : _("Not enough memory"));
493 : 0 : return G_CONVERTER_ERROR;
494 : : }
495 : :
496 : 4454 : if (res == Z_STREAM_ERROR)
497 : : {
498 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
499 : : _("Internal error: %s"), compressor->zstream.msg);
500 : 0 : return G_CONVERTER_ERROR;
501 : : }
502 : :
503 : 4454 : if (res == Z_BUF_ERROR)
504 : : {
505 : 4 : if (flags & G_CONVERTER_FLUSH)
506 : 4 : return G_CONVERTER_FLUSHED;
507 : :
508 : : /* We do have output space, so this should only happen if we
509 : : have no input but need some */
510 : :
511 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
512 : : _("Need more input"));
513 : 0 : return G_CONVERTER_ERROR;
514 : : }
515 : :
516 : 4450 : if (res == Z_OK || res == Z_STREAM_END)
517 : : {
518 : 4450 : *bytes_read = inbuf_size - compressor->zstream.avail_in;
519 : 4450 : *bytes_written = outbuf_size - compressor->zstream.avail_out;
520 : :
521 : 4450 : if (res == Z_STREAM_END)
522 : 21 : return G_CONVERTER_FINISHED;
523 : 4429 : return G_CONVERTER_CONVERTED;
524 : : }
525 : :
526 : : g_assert_not_reached ();
527 : : }
528 : :
529 : : static void
530 : 7 : g_zlib_compressor_iface_init (GConverterIface *iface)
531 : : {
532 : 7 : iface->convert = g_zlib_compressor_convert;
533 : 7 : iface->reset = g_zlib_compressor_reset;
534 : 7 : }
|