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 
21 static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
22 static 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 
32 int az_open(azio_stream *s, const char *path, int Flags, File  fd);
33 int do_flush(azio_stream *file, int flush);
34 int    get_byte(azio_stream *s);
35 void   check_header(azio_stream *s);
36 int write_header(azio_stream *s);
37 int    destroy(azio_stream *s);
38 void putLong(File file, uLong x);
39 uLong  getLong(azio_stream *s);
40 void read_header(azio_stream *s, unsigned char *buffer);
41 
42 #ifdef HAVE_PSI_INTERFACE
43 extern 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 */
az_open(azio_stream * s,const char * path,int Flags,File fd)55 int 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 
write_header(azio_stream * s)185 int 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 */
azopen(azio_stream * s,const char * path,int Flags)231 int 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 */
azdopen(azio_stream * s,File fd,int Flags)240 int 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 */
get_byte(s)252 int 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 */
check_header(azio_stream * s)287 void 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 
read_header(azio_stream * s,unsigned char * buffer)368 void 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  */
destroy(s)430 int 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 */
azread(azio_stream * s,voidp buf,size_t len,int * error)457 unsigned 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 */
azwrite(azio_stream * s,const voidp buf,unsigned int len)585 unsigned 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 */
do_flush(azio_stream * s,int flush)629 int 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 
azflush(s,flush)682 int 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 */
azrewind(s)711 int 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 */
azseek(s,offset,whence)736 my_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 */
aztell(file)815 my_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 */
putLong(File file,uLong x)825 void 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 */
getLong(azio_stream * s)842 uLong 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 */
azclose(azio_stream * s)859 int 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 */
azwrite_frm(azio_stream * s,const uchar * blob,size_t length)885 int 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 
azread_frm(azio_stream * s,uchar * blob)906 int 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 */
azwrite_comment(azio_stream * s,const char * blob,size_t length)916 int 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 
azread_comment(azio_stream * s,char * blob)937 int 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