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