1/*
2 azio is a modified version of gzio. It makes use of mysys and removes mallocs.
3 -Brian Aker
4*/
5
6/* gzio.c -- IO on .gz files
7 * Copyright (C) 1995-2005 Jean-loup Gailly.
8 * For conditions of distribution and use, see copyright notice in zlib.h
9 *
10 */
11
12/* @(#) $Id$ */
13
14#include "azlib.h"
15
16#include <stdio.h>
17#include <string.h>
18
19#include "my_sys.h"
20
21static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
22static int const az_magic[3] = {0xfe, 0x03, 0x01}; /* az magic header */
23
24/* gzip flag uchar */
25#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
26#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
27#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
28#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
29#define COMMENT 0x10 /* bit 4 set: file comment present */
30#define RESERVED 0xE0 /* bits 5..7: reserved */
31
32int az_open(azio_stream *s, const char *path, int Flags, File fd);
33int do_flush(azio_stream *file, int flush);
34int get_byte(azio_stream *s);
35void check_header(azio_stream *s);
36int write_header(azio_stream *s);
37int destroy(azio_stream *s);
38void putLong(File file, uLong x);
39uLong getLong(azio_stream *s);
40void read_header(azio_stream *s, unsigned char *buffer);
41
42#ifdef HAVE_PSI_INTERFACE
43extern PSI_file_key arch_key_file_data;
44#endif
45
46/* ===========================================================================
47 Opens a gzip (.gz) file for reading or writing. The mode parameter
48 is as in fopen ("rb" or "wb"). The file is given either by file descriptor
49 or path name (if fd == -1).
50 az_open returns NULL if the file could not be opened or if there was
51 insufficient memory to allocate the (de)compression state; errno
52 can be checked to distinguish the two cases (if errno is zero, the
53 zlib error is Z_MEM_ERROR).
54*/
55int az_open (azio_stream *s, const char *path, int Flags, File fd)
56{
57 int err;
58 int level = Z_DEFAULT_COMPRESSION; /* compression level */
59 int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
60
61 s->stream.zalloc = my_az_allocator;
62 s->stream.zfree = my_az_free;
63 s->stream.opaque = (voidpf)0;
64 memset(s->inbuf, 0, AZ_BUFSIZE_READ);
65 memset(s->outbuf, 0, AZ_BUFSIZE_WRITE);
66 s->stream.next_in = s->inbuf;
67 s->stream.next_out = s->outbuf;
68 s->stream.avail_in = s->stream.avail_out = 0;
69 s->z_err = Z_OK;
70 s->z_eof = 0;
71 s->in = 0;
72 s->out = 0;
73 s->back = EOF;
74 s->crc = crc32(0L, Z_NULL, 0);
75 s->transparent = 0;
76 s->mode = 'r';
77 s->version = (unsigned char)az_magic[1]; /* this needs to be a define to version */
78 s->minor_version= (unsigned char) az_magic[2]; /* minor version */
79 s->dirty= AZ_STATE_CLEAN;
80 s->start= 0;
81
82 /*
83 We do our own version of append by nature.
84 We must always have write access to take card of the header.
85 */
86 DBUG_ASSERT(Flags | O_APPEND);
87 DBUG_ASSERT(Flags | O_WRONLY);
88
89 if (Flags & O_RDWR)
90 s->mode = 'w';
91
92 if (s->mode == 'w')
93 {
94 err = deflateInit2(&(s->stream), level,
95 Z_DEFLATED, -MAX_WBITS, 8, strategy);
96 /* windowBits is passed < 0 to suppress zlib header */
97
98 s->stream.next_out = s->outbuf;
99 if (err != Z_OK)
100 {
101 destroy(s);
102 return Z_NULL;
103 }
104 } else {
105 s->stream.next_in = s->inbuf;
106
107 err = inflateInit2(&(s->stream), -MAX_WBITS);
108 /* windowBits is passed < 0 to tell that there is no zlib header.
109 * Note that in this case inflate *requires* an extra "dummy" byte
110 * after the compressed stream in order to complete decompression and
111 * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
112 * present after the compressed stream.
113 */
114 if (err != Z_OK)
115 {
116 destroy(s);
117 return Z_NULL;
118 }
119 }
120 s->stream.avail_out = AZ_BUFSIZE_WRITE;
121
122 errno = 0;
123 s->file = fd < 0 ? mysql_file_open(arch_key_file_data, path, Flags, MYF(0)) : fd;
124 DBUG_EXECUTE_IF("simulate_archive_open_failure",
125 {
126 if (s->file >= 0)
127 {
128 my_close(s->file, MYF(0));
129 s->file= -1;
130 my_errno= EMFILE;
131 }
132 });
133
134 if (s->file < 0 )
135 {
136 destroy(s);
137 return Z_NULL;
138 }
139
140 if (Flags & O_CREAT || Flags & O_TRUNC)
141 {
142 s->rows= 0;
143 s->forced_flushes= 0;
144 s->shortest_row= 0;
145 s->longest_row= 0;
146 s->auto_increment= 0;
147 s->check_point= 0;
148 s->comment_start_pos= 0;
149 s->comment_length= 0;
150 s->frm_start_pos= 0;
151 s->frm_length= 0;
152 s->dirty= 1; /* We create the file dirty */
153 s->start = AZHEADER_SIZE + AZMETA_BUFFER_SIZE;
154 write_header(s);
155 my_seek(s->file, 0, MY_SEEK_END, MYF(0));
156 }
157 else if (s->mode == 'w')
158 {
159 uchar buffer[AZHEADER_SIZE + AZMETA_BUFFER_SIZE];
160 my_pread(s->file, buffer, AZHEADER_SIZE + AZMETA_BUFFER_SIZE, 0,
161 MYF(0));
162 read_header(s, buffer); /* skip the .az header */
163 my_seek(s->file, 0, MY_SEEK_END, MYF(0));
164 }
165 else
166 {
167 /* Reset values in case of old version of archive file */
168 s->rows= 0;
169 s->forced_flushes= 0;
170 s->shortest_row= 0;
171 s->longest_row= 0;
172 s->auto_increment= 0;
173 s->check_point= 0;
174 s->comment_start_pos= 0;
175 s->comment_length= 0;
176 s->frm_start_pos= 0;
177 s->frm_length= 0;
178 check_header(s); /* skip the .az header */
179 }
180
181 return 1;
182}
183
184
185int write_header(azio_stream *s)
186{
187 char buffer[AZHEADER_SIZE + AZMETA_BUFFER_SIZE];
188 char *ptr= buffer;
189
190 if (s->version == 1)
191 return 0;
192
193 s->block_size= AZ_BUFSIZE_WRITE;
194 s->version = (unsigned char)az_magic[1];
195 s->minor_version = (unsigned char)az_magic[2];
196
197
198 /* Write a very simple .az header: */
199 memset(buffer, 0, AZHEADER_SIZE + AZMETA_BUFFER_SIZE);
200 *(ptr + AZ_MAGIC_POS)= az_magic[0];
201 *(ptr + AZ_VERSION_POS)= (unsigned char)s->version;
202 *(ptr + AZ_MINOR_VERSION_POS)= (unsigned char)s->minor_version;
203 *(ptr + AZ_BLOCK_POS)= (unsigned char)(s->block_size/1024); /* Reserved for block size */
204 *(ptr + AZ_STRATEGY_POS)= (unsigned char)Z_DEFAULT_STRATEGY; /* Compression Type */
205
206 int4store(ptr + AZ_FRM_POS, s->frm_start_pos); /* FRM Block */
207 int4store(ptr + AZ_FRM_LENGTH_POS, s->frm_length); /* FRM Block */
208 int4store(ptr + AZ_COMMENT_POS, s->comment_start_pos); /* COMMENT Block */
209 int4store(ptr + AZ_COMMENT_LENGTH_POS, s->comment_length); /* COMMENT Block */
210 int4store(ptr + AZ_META_POS, 0); /* Meta Block */
211 int4store(ptr + AZ_META_LENGTH_POS, 0); /* Meta Block */
212 int8store(ptr + AZ_START_POS, (unsigned long long)s->start); /* Start of Data Block Index Block */
213 int8store(ptr + AZ_ROW_POS, (unsigned long long)s->rows); /* Start of Data Block Index Block */
214 int8store(ptr + AZ_FLUSH_POS, (unsigned long long)s->forced_flushes); /* Start of Data Block Index Block */
215 int8store(ptr + AZ_CHECK_POS, (unsigned long long)s->check_point); /* Start of Data Block Index Block */
216 int8store(ptr + AZ_AUTOINCREMENT_POS, (unsigned long long)s->auto_increment); /* Start of Data Block Index Block */
217 int4store(ptr+ AZ_LONGEST_POS , s->longest_row); /* Longest row */
218 int4store(ptr+ AZ_SHORTEST_POS, s->shortest_row); /* Shorest row */
219 int4store(ptr+ AZ_FRM_POS,
220 AZHEADER_SIZE + AZMETA_BUFFER_SIZE); /* FRM position */
221 *(ptr + AZ_DIRTY_POS)= (unsigned char)s->dirty; /* Start of Data Block Index Block */
222
223 /* Always begin at the beginning, and end there as well */
224 return my_pwrite(s->file, (uchar*) buffer, AZHEADER_SIZE + AZMETA_BUFFER_SIZE,
225 0, MYF(MY_NABP)) ? 1 : 0;
226}
227
228/* ===========================================================================
229 Opens a gzip (.gz) file for reading or writing.
230*/
231int azopen(azio_stream *s, const char *path, int Flags)
232{
233 return az_open(s, path, Flags, -1);
234}
235
236/* ===========================================================================
237 Associate a gzFile with the file descriptor fd. fd is not dup'ed here
238 to mimic the behavio(u)r of fdopen.
239*/
240int azdopen(azio_stream *s, File fd, int Flags)
241{
242 if (fd < 0) return 0;
243
244 return az_open (s, NULL, Flags, fd);
245}
246
247/* ===========================================================================
248 Read a byte from a azio_stream; update next_in and avail_in. Return EOF
249 for end of file.
250 IN assertion: the stream s has been sucessfully opened for reading.
251*/
252int get_byte(s)
253 azio_stream *s;
254{
255 if (s->z_eof) return EOF;
256 if (s->stream.avail_in == 0)
257 {
258 errno = 0;
259 s->stream.avail_in= (uInt) mysql_file_read(s->file, (uchar *)s->inbuf,
260 AZ_BUFSIZE_READ, MYF(0));
261 if (s->stream.avail_in == 0)
262 {
263 s->z_eof = 1;
264 return EOF;
265 }
266 else if (s->stream.avail_in == (uInt) -1)
267 {
268 s->z_eof= 1;
269 s->z_err= Z_ERRNO;
270 return EOF;
271 }
272 s->stream.next_in = s->inbuf;
273 }
274 s->stream.avail_in--;
275 return *(s->stream.next_in)++;
276}
277
278/* ===========================================================================
279 Check the gzip header of a azio_stream opened for reading. Set the stream
280 mode to transparent if the gzip magic header is not present; set s->err
281 to Z_DATA_ERROR if the magic header is present but the rest of the header
282 is incorrect.
283 IN assertion: the stream s has already been created sucessfully;
284 s->stream.avail_in is zero for the first time, but may be non-zero
285 for concatenated .gz files.
286*/
287void check_header(azio_stream *s)
288{
289 int method; /* method uchar */
290 int flags; /* flags uchar */
291 uInt len;
292 int c;
293
294 /* Assure two bytes in the buffer so we can peek ahead -- handle case
295 where first byte of header is at the end of the buffer after the last
296 gzip segment */
297 len = s->stream.avail_in;
298 if (len < 2) {
299 if (len) s->inbuf[0] = s->stream.next_in[0];
300 errno = 0;
301 len = (uInt)mysql_file_read(s->file, (uchar *)s->inbuf + len,
302 AZ_BUFSIZE_READ >> len, MYF(0));
303 if (len == (uInt)-1) s->z_err = Z_ERRNO;
304 s->stream.avail_in += len;
305 s->stream.next_in = s->inbuf;
306 if (s->stream.avail_in < 2) {
307 s->transparent = s->stream.avail_in;
308 return;
309 }
310 }
311
312 /* Peek ahead to check the gzip magic header */
313 if ( s->stream.next_in[0] == gz_magic[0] && s->stream.next_in[1] == gz_magic[1])
314 {
315 read_header(s, s->stream.next_in);
316 s->stream.avail_in -= 2;
317 s->stream.next_in += 2;
318
319 /* Check the rest of the gzip header */
320 method = get_byte(s);
321 flags = get_byte(s);
322 if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
323 s->z_err = Z_DATA_ERROR;
324 return;
325 }
326
327 /* Discard time, xflags and OS code: */
328 for (len = 0; len < 6; len++) (void)get_byte(s);
329
330 if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
331 len = (uInt)get_byte(s);
332 len += ((uInt)get_byte(s))<<8;
333 /* len is garbage if EOF but the loop below will quit anyway */
334 while (len-- != 0 && get_byte(s) != EOF) ;
335 }
336 if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
337 while ((c = get_byte(s)) != 0 && c != EOF) ;
338 }
339 if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
340 while ((c = get_byte(s)) != 0 && c != EOF) ;
341 }
342 if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
343 for (len = 0; len < 2; len++) (void)get_byte(s);
344 }
345 s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
346 if (!s->start)
347 s->start= my_tell(s->file, MYF(0)) - s->stream.avail_in;
348 }
349 else if ( s->stream.next_in[0] == az_magic[0] && s->stream.next_in[1] == az_magic[1])
350 {
351 unsigned char buffer[AZHEADER_SIZE + AZMETA_BUFFER_SIZE];
352
353 for (len = 0; len < (AZHEADER_SIZE + AZMETA_BUFFER_SIZE); len++)
354 buffer[len]= get_byte(s);
355 s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
356 read_header(s, buffer);
357 for (; len < s->start; len++)
358 get_byte(s);
359 }
360 else
361 {
362 s->z_err = Z_OK;
363
364 return;
365 }
366}
367
368void read_header(azio_stream *s, unsigned char *buffer)
369{
370 if (buffer[0] == az_magic[0] && buffer[1] == az_magic[1])
371 {
372 uchar tmp[AZ_FRMVER_LEN + 2];
373
374 s->version= (unsigned int)buffer[AZ_VERSION_POS];
375 s->minor_version= (unsigned int)buffer[AZ_MINOR_VERSION_POS];
376 s->block_size= 1024 * buffer[AZ_BLOCK_POS];
377 s->start= (unsigned long long)uint8korr(buffer + AZ_START_POS);
378 s->rows= (unsigned long long)uint8korr(buffer + AZ_ROW_POS);
379 s->check_point= (unsigned long long)uint8korr(buffer + AZ_CHECK_POS);
380 s->forced_flushes= (unsigned long long)uint8korr(buffer + AZ_FLUSH_POS);
381 s->auto_increment= (unsigned long long)uint8korr(buffer + AZ_AUTOINCREMENT_POS);
382 s->longest_row= (unsigned int)uint4korr(buffer + AZ_LONGEST_POS);
383 s->shortest_row= (unsigned int)uint4korr(buffer + AZ_SHORTEST_POS);
384 s->frm_start_pos= (unsigned int)uint4korr(buffer + AZ_FRM_POS);
385 s->frm_length= (unsigned int)uint4korr(buffer + AZ_FRM_LENGTH_POS);
386 s->comment_start_pos= (unsigned int)uint4korr(buffer + AZ_COMMENT_POS);
387 s->comment_length= (unsigned int)uint4korr(buffer + AZ_COMMENT_LENGTH_POS);
388 s->dirty= (unsigned int)buffer[AZ_DIRTY_POS];
389
390 /*
391 we'll hard-code the current frm format for now, to avoid
392 changing archive table versions.
393 */
394 if (s->frm_length == 0 ||
395 my_pread(s->file, tmp, sizeof(tmp), s->frm_start_pos + 64, MYF(MY_NABP)) ||
396 tmp[0] != 0 || tmp[1] != AZ_FRMVER_LEN)
397 {
398 s->frmver_length= 0;
399 }
400 else
401 {
402 s->frmver_length= tmp[1];
403 memcpy(s->frmver, tmp+2, s->frmver_length);
404 }
405 }
406 else if (buffer[0] == gz_magic[0] && buffer[1] == gz_magic[1])
407 {
408 /*
409 Set version number to previous version (1).
410 */
411 s->version= 1;
412 s->auto_increment= 0;
413 s->frm_length= 0;
414 s->longest_row= 0;
415 s->shortest_row= 0;
416 } else {
417 /*
418 Unknown version.
419 Most probably due to a corrupt archive.
420 */
421 s->dirty= AZ_STATE_DIRTY;
422 s->z_err= Z_VERSION_ERROR;
423 }
424}
425
426/* ===========================================================================
427 * Cleanup then free the given azio_stream. Return a zlib error code.
428 Try freeing in the reverse order of allocations.
429 */
430int destroy (s)
431 azio_stream *s;
432{
433 int err = Z_OK;
434
435 if (s->stream.state != NULL)
436 {
437 if (s->mode == 'w')
438 err = deflateEnd(&(s->stream));
439 else if (s->mode == 'r')
440 err = inflateEnd(&(s->stream));
441 }
442
443 if (s->file > 0 && my_close(s->file, MYF(0)))
444 err = Z_ERRNO;
445
446 s->file= -1;
447
448 if (s->z_err < 0) err = s->z_err;
449
450 return err;
451}
452
453/* ===========================================================================
454 Reads the given number of uncompressed bytes from the compressed file.
455 azread returns the number of bytes actually read (0 for end of file).
456*/
457unsigned int ZEXPORT azread ( azio_stream *s, voidp buf, size_t len, int *error)
458{
459 Bytef *start = (Bytef*)buf; /* starting point for crc computation */
460 Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */
461 *error= 0;
462
463 if (s->mode != 'r')
464 {
465 *error= Z_STREAM_ERROR;
466 return 0;
467 }
468
469 if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)
470 {
471 *error= s->z_err;
472 return 0;
473 }
474
475 if (s->z_err == Z_STREAM_END) /* EOF */
476 {
477 return 0;
478 }
479
480 next_out = (Byte*)buf;
481 s->stream.next_out = (Bytef*)buf;
482 s->stream.avail_out = (uInt)len;
483
484 if (s->stream.avail_out && s->back != EOF) {
485 *next_out++ = s->back;
486 s->stream.next_out++;
487 s->stream.avail_out--;
488 s->back = EOF;
489 s->out++;
490 start++;
491 if (s->last) {
492 s->z_err = Z_STREAM_END;
493 {
494 return 1;
495 }
496 }
497 }
498
499 while (s->stream.avail_out != 0) {
500
501 if (s->transparent) {
502 /* Copy first the lookahead bytes: */
503 uInt n = s->stream.avail_in;
504 if (n > s->stream.avail_out) n = s->stream.avail_out;
505 if (n > 0) {
506 memcpy(s->stream.next_out, s->stream.next_in, n);
507 next_out += n;
508 s->stream.next_out = (Bytef *)next_out;
509 s->stream.next_in += n;
510 s->stream.avail_out -= n;
511 s->stream.avail_in -= n;
512 }
513 if (s->stream.avail_out > 0)
514 {
515 s->stream.avail_out -=
516 (uInt)mysql_file_read(s->file, (uchar *)next_out,
517 s->stream.avail_out, MYF(0));
518 }
519 len -= s->stream.avail_out;
520 s->in += len;
521 s->out += len;
522 if (len == 0) s->z_eof = 1;
523 {
524 return (uint)len;
525 }
526 }
527 if (s->stream.avail_in == 0 && !s->z_eof) {
528
529 errno = 0;
530 s->stream.avail_in = (uInt)mysql_file_read(s->file, (uchar *)s->inbuf,
531 AZ_BUFSIZE_READ, MYF(0));
532 if (s->stream.avail_in == 0)
533 {
534 s->z_eof = 1;
535 }
536 s->stream.next_in = (Bytef *)s->inbuf;
537 }
538 s->in += s->stream.avail_in;
539 s->out += s->stream.avail_out;
540 s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
541 s->in -= s->stream.avail_in;
542 s->out -= s->stream.avail_out;
543
544 if (s->z_err == Z_STREAM_END) {
545 /* Check CRC and original size */
546 s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
547 start = s->stream.next_out;
548
549 if (getLong(s) != s->crc) {
550 s->z_err = Z_DATA_ERROR;
551 } else {
552 (void)getLong(s);
553 /* The uncompressed length returned by above getlong() may be
554 * different from s->out in case of concatenated .gz files.
555 * Check for such files:
556 */
557 check_header(s);
558 if (s->z_err == Z_OK)
559 {
560 inflateReset(&(s->stream));
561 s->crc = crc32(0L, Z_NULL, 0);
562 }
563 }
564 }
565 if (s->z_err != Z_OK || s->z_eof) break;
566 }
567 s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
568
569 if (len == s->stream.avail_out &&
570 (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO))
571 {
572 *error= s->z_err;
573
574 return 0;
575 }
576
577 return (uint)(len - s->stream.avail_out);
578}
579
580
581/* ===========================================================================
582 Writes the given number of uncompressed bytes into the compressed file.
583 azwrite returns the number of bytes actually written (0 in case of error).
584*/
585unsigned int azwrite (azio_stream *s, const voidp buf, unsigned int len)
586{
587 s->stream.next_in = (Bytef*)buf;
588 s->stream.avail_in = len;
589
590 s->rows++;
591
592 while (s->stream.avail_in != 0)
593 {
594 if (s->stream.avail_out == 0)
595 {
596
597 s->stream.next_out = s->outbuf;
598 if (mysql_file_write(s->file, (uchar *)s->outbuf, AZ_BUFSIZE_WRITE,
599 MYF(0)) != AZ_BUFSIZE_WRITE)
600 {
601 s->z_err = Z_ERRNO;
602 break;
603 }
604 s->stream.avail_out = AZ_BUFSIZE_WRITE;
605 }
606 s->in += s->stream.avail_in;
607 s->out += s->stream.avail_out;
608 s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
609 s->in -= s->stream.avail_in;
610 s->out -= s->stream.avail_out;
611 if (s->z_err != Z_OK) break;
612 }
613 s->crc = crc32(s->crc, (const Bytef *)buf, len);
614
615 if (len > s->longest_row)
616 s->longest_row= len;
617
618 if (len < s->shortest_row || !(s->shortest_row))
619 s->shortest_row= len;
620
621 return (unsigned int)(len - s->stream.avail_in);
622}
623
624
625/* ===========================================================================
626 Flushes all pending output into the compressed file. The parameter
627 flush is as in the deflate() function.
628*/
629int do_flush (azio_stream *s, int flush)
630{
631 uInt len;
632 int done = 0;
633 my_off_t afterwrite_pos;
634
635 if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
636
637 s->stream.avail_in = 0; /* should be zero already anyway */
638
639 for (;;)
640 {
641 len = AZ_BUFSIZE_WRITE - s->stream.avail_out;
642
643 if (len != 0)
644 {
645 s->check_point= my_tell(s->file, MYF(0));
646 if ((uInt)mysql_file_write(s->file, (uchar *)s->outbuf, len, MYF(0)) != len)
647 {
648 s->z_err = Z_ERRNO;
649 return Z_ERRNO;
650 }
651 s->stream.next_out = s->outbuf;
652 s->stream.avail_out = AZ_BUFSIZE_WRITE;
653 }
654 if (done) break;
655 s->out += s->stream.avail_out;
656 s->z_err = deflate(&(s->stream), flush);
657 s->out -= s->stream.avail_out;
658
659 /* Ignore the second of two consecutive flushes: */
660 if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
661
662 /* deflate has finished flushing only when it hasn't used up
663 * all the available space in the output buffer:
664 */
665 done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
666
667 if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
668 }
669
670 if (flush == Z_FINISH)
671 s->dirty= AZ_STATE_CLEAN; /* Mark it clean, we should be good now */
672 else
673 s->dirty= AZ_STATE_SAVED; /* Mark it clean, we should be good now */
674
675 afterwrite_pos= my_tell(s->file, MYF(0));
676 write_header(s);
677 my_seek(s->file, afterwrite_pos, SEEK_SET, MYF(0));
678
679 return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
680}
681
682int ZEXPORT azflush (s, flush)
683 azio_stream *s;
684 int flush;
685{
686 int err;
687
688 if (s->mode == 'r')
689 {
690 unsigned char buffer[AZHEADER_SIZE + AZMETA_BUFFER_SIZE];
691 my_pread(s->file, (uchar*) buffer, AZHEADER_SIZE + AZMETA_BUFFER_SIZE, 0,
692 MYF(0));
693 read_header(s, buffer); /* skip the .az header */
694
695 return Z_OK;
696 }
697 else
698 {
699 s->forced_flushes++;
700 err= do_flush(s, flush);
701
702 if (err) return err;
703 my_sync(s->file, MYF(0));
704 return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
705 }
706}
707
708/* ===========================================================================
709 Rewinds input file.
710*/
711int azrewind (s)
712 azio_stream *s;
713{
714 if (s == NULL || s->mode != 'r') return -1;
715
716 s->z_err = Z_OK;
717 s->z_eof = 0;
718 s->back = EOF;
719 s->stream.avail_in = 0;
720 s->stream.next_in = (Bytef *)s->inbuf;
721 s->crc = crc32(0L, Z_NULL, 0);
722 if (!s->transparent) (void)inflateReset(&s->stream);
723 s->in = 0;
724 s->out = 0;
725 return my_seek(s->file, (int)s->start, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR;
726}
727
728/* ===========================================================================
729 Sets the starting position for the next azread or azwrite on the given
730 compressed file. The offset represents a number of bytes in the
731 azseek returns the resulting offset location as measured in bytes from
732 the beginning of the uncompressed stream, or -1 in case of error.
733 SEEK_END is not implemented, returns error.
734 In this version of the library, azseek can be extremely slow.
735*/
736my_off_t azseek (s, offset, whence)
737 azio_stream *s;
738 my_off_t offset;
739 int whence;
740{
741
742 if (s == NULL || whence == SEEK_END ||
743 s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) {
744 return -1L;
745 }
746
747 if (s->mode == 'w')
748 {
749 if (whence == SEEK_SET)
750 offset -= s->in;
751
752 /* At this point, offset is the number of zero bytes to write. */
753 /* There was a zmemzero here if inbuf was null -Brian */
754 while (offset > 0)
755 {
756 uInt size = AZ_BUFSIZE_READ;
757 if (offset < AZ_BUFSIZE_READ) size = (uInt)offset;
758
759 size = azwrite(s, s->inbuf, size);
760 if (size == 0) return -1L;
761
762 offset -= size;
763 }
764 return s->in;
765 }
766 /* Rest of function is for reading only */
767
768 /* compute absolute position */
769 if (whence == SEEK_CUR) {
770 offset += s->out;
771 }
772
773 if (s->transparent) {
774 /* map to my_seek */
775 s->back = EOF;
776 s->stream.avail_in = 0;
777 s->stream.next_in = (Bytef *)s->inbuf;
778 if (my_seek(s->file, offset, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR) return -1L;
779
780 s->in = s->out = offset;
781 return offset;
782 }
783
784 /* For a negative seek, rewind and use positive seek */
785 if (offset >= s->out) {
786 offset -= s->out;
787 } else if (azrewind(s)) {
788 return -1L;
789 }
790 /* offset is now the number of bytes to skip. */
791
792 if (offset && s->back != EOF) {
793 s->back = EOF;
794 s->out++;
795 offset--;
796 if (s->last) s->z_err = Z_STREAM_END;
797 }
798 while (offset > 0) {
799 int error;
800 unsigned int size = AZ_BUFSIZE_WRITE;
801 if (offset < AZ_BUFSIZE_WRITE) size = (int)offset;
802
803 size = azread(s, s->outbuf, size, &error);
804 if (error < 0) return -1L;
805 offset -= size;
806 }
807 return s->out;
808}
809
810/* ===========================================================================
811 Returns the starting position for the next azread or azwrite on the
812 given compressed file. This position represents a number of bytes in the
813 uncompressed data stream.
814*/
815my_off_t ZEXPORT aztell (file)
816 azio_stream *file;
817{
818 return azseek(file, 0L, SEEK_CUR);
819}
820
821
822/* ===========================================================================
823 Outputs a long in LSB order to the given file
824*/
825void putLong (File file, uLong x)
826{
827 int n;
828 uchar buffer[1];
829
830 for (n = 0; n < 4; n++)
831 {
832 buffer[0]= (int)(x & 0xff);
833 mysql_file_write(file, buffer, 1, MYF(0));
834 x >>= 8;
835 }
836}
837
838/* ===========================================================================
839 Reads a long in LSB order from the given azio_stream. Sets z_err in case
840 of error.
841*/
842uLong getLong (azio_stream *s)
843{
844 uLong x = (uLong)get_byte(s);
845 int c;
846
847 x += ((uLong)get_byte(s))<<8;
848 x += ((uLong)get_byte(s))<<16;
849 c = get_byte(s);
850 if (c == EOF) s->z_err = Z_DATA_ERROR;
851 x += ((uLong)c)<<24;
852 return x;
853}
854
855/* ===========================================================================
856 Flushes all pending output if necessary, closes the compressed file
857 and deallocates all the (de)compression state.
858*/
859int azclose (azio_stream *s)
860{
861
862 if (s == NULL) return Z_STREAM_ERROR;
863
864 if (s->file < 1) return Z_OK;
865
866 if (s->mode == 'w')
867 {
868 if (do_flush(s, Z_FINISH) != Z_OK)
869 return destroy(s);
870
871 putLong(s->file, s->crc);
872 putLong(s->file, (uLong)(s->in & 0xffffffff));
873 s->dirty= AZ_STATE_CLEAN;
874 s->check_point= my_tell(s->file, MYF(0));
875 write_header(s);
876 }
877
878 return destroy(s);
879}
880
881/*
882 Though this was added to support MySQL's FRM file, anything can be
883 stored in this location.
884*/
885int azwrite_frm(azio_stream *s, const uchar *blob, size_t length)
886{
887 if (s->mode == 'r')
888 return 1;
889
890 if (s->rows > 0)
891 return 1;
892
893 s->frm_start_pos= (uint) s->start;
894 s->frm_length= (uint)length;
895 s->start+= length;
896
897 if (my_pwrite(s->file, blob, s->frm_length,
898 s->frm_start_pos, MYF(MY_NABP)) ||
899 write_header(s) ||
900 (my_seek(s->file, 0, MY_SEEK_END, MYF(0)) == MY_FILEPOS_ERROR))
901 return 1;
902
903 return 0;
904}
905
906int azread_frm(azio_stream *s, uchar *blob)
907{
908 return my_pread(s->file, blob, s->frm_length,
909 s->frm_start_pos, MYF(MY_NABP)) ? 1 : 0;
910}
911
912
913/*
914 Simple comment field
915*/
916int azwrite_comment(azio_stream *s, const char *blob, size_t length)
917{
918 if (s->mode == 'r')
919 return 1;
920
921 if (s->rows > 0)
922 return 1;
923
924 s->comment_start_pos= (uint) s->start;
925 s->comment_length= (uint)length;
926 s->start+= length;
927
928 my_pwrite(s->file, (uchar*) blob, s->comment_length, s->comment_start_pos,
929 MYF(0));
930
931 write_header(s);
932 my_seek(s->file, 0, MY_SEEK_END, MYF(0));
933
934 return 0;
935}
936
937int azread_comment(azio_stream *s, char *blob)
938{
939 my_pread(s->file, (uchar*) blob, s->comment_length, s->comment_start_pos,
940 MYF(0));
941
942 return 0;
943}
944