]> granicus.if.org Git - postgresql/blob - src/bin/pg_dump/pg_backup_custom.c
Speed improvement for large object restore.
[postgresql] / src / bin / pg_dump / pg_backup_custom.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_backup_custom.c
4  *
5  *      Implements the custom output format.
6  *
7  *      The comments with the routined in this code are a good place to
8  *      understand how to write a new format.
9  *
10  *      See the headers to pg_restore for more details.
11  *
12  * Copyright (c) 2000, Philip Warner
13  *              Rights are granted to use this software in any way so long
14  *              as this notice is not removed.
15  *
16  *      The author is not responsible for loss or damages that may
17  *      and any liability will be limited to the time taken to fix any
18  *      related bug.
19  *
20  *
21  * IDENTIFICATION
22  *              $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_custom.c,v 1.18 2002/04/24 02:21:04 momjian Exp $
23  *
24  * Modifications - 28-Jun-2000 - pjw@rhyme.com.au
25  *
26  *      Initial version.
27  *
28  * Modifications - 04-Jan-2001 - pjw@rhyme.com.au
29  *
30  *        - Check results of IO routines more carefully.
31  *
32  *-------------------------------------------------------------------------
33  */
34
35 #include "pg_backup.h"
36 #include "pg_backup_archiver.h"
37
38 #include <errno.h>
39
40 /*--------
41  * Routines in the format interface
42  *--------
43  */
44
45 static void _ArchiveEntry(ArchiveHandle *AH, TocEntry *te);
46 static void _StartData(ArchiveHandle *AH, TocEntry *te);
47 static int      _WriteData(ArchiveHandle *AH, const void *data, int dLen);
48 static void _EndData(ArchiveHandle *AH, TocEntry *te);
49 static int      _WriteByte(ArchiveHandle *AH, const int i);
50 static int      _ReadByte(ArchiveHandle *);
51 static int      _WriteBuf(ArchiveHandle *AH, const void *buf, int len);
52 static int      _ReadBuf(ArchiveHandle *AH, void *buf, int len);
53 static void _CloseArchive(ArchiveHandle *AH);
54 static void _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
55 static void _WriteExtraToc(ArchiveHandle *AH, TocEntry *te);
56 static void _ReadExtraToc(ArchiveHandle *AH, TocEntry *te);
57 static void _PrintExtraToc(ArchiveHandle *AH, TocEntry *te);
58
59 static void _PrintData(ArchiveHandle *AH);
60 static void _skipData(ArchiveHandle *AH);
61 static void _skipBlobs(ArchiveHandle *AH);
62
63 static void _StartBlobs(ArchiveHandle *AH, TocEntry *te);
64 static void _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
65 static void _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
66 static void _EndBlobs(ArchiveHandle *AH, TocEntry *te);
67 static void _LoadBlobs(ArchiveHandle *AH);
68
69 /*------------
70  * Buffers used in zlib compression and extra data stored in archive and
71  * in TOC entries.
72  *------------
73  */
74 #define zlibOutSize 4096
75 #define zlibInSize      4096
76
77 typedef struct
78 {
79         z_streamp       zp;
80         char       *zlibOut;
81         char       *zlibIn;
82         int                     inSize;
83         int                     hasSeek;
84         int                     filePos;
85         int                     dataStart;
86 } lclContext;
87
88 typedef struct
89 {
90         int                     dataPos;
91         int                     dataLen;
92 } lclTocEntry;
93
94
95 /*------
96  * Static declarations
97  *------
98  */
99 static void _readBlockHeader(ArchiveHandle *AH, int *type, int *id);
100 static void _StartDataCompressor(ArchiveHandle *AH, TocEntry *te);
101 static void _EndDataCompressor(ArchiveHandle *AH, TocEntry *te);
102 static int      _getFilePos(ArchiveHandle *AH, lclContext *ctx);
103 static int      _DoDeflate(ArchiveHandle *AH, lclContext *ctx, int flush);
104
105 static char *modulename = gettext_noop("custom archiver");
106
107
108
109 /*
110  *      Init routine required by ALL formats. This is a global routine
111  *      and should be declared in pg_backup_archiver.h
112  *
113  *      It's task is to create any extra archive context (using AH->formatData),
114  *      and to initialize the supported function pointers.
115  *
116  *      It should also prepare whatever it's input source is for reading/writing,
117  *      and in the case of a read mode connection, it should load the Header & TOC.
118  */
119 void
120 InitArchiveFmt_Custom(ArchiveHandle *AH)
121 {
122         lclContext *ctx;
123
124         /* Assuming static functions, this can be copied for each format. */
125         AH->ArchiveEntryPtr = _ArchiveEntry;
126         AH->StartDataPtr = _StartData;
127         AH->WriteDataPtr = _WriteData;
128         AH->EndDataPtr = _EndData;
129         AH->WriteBytePtr = _WriteByte;
130         AH->ReadBytePtr = _ReadByte;
131         AH->WriteBufPtr = _WriteBuf;
132         AH->ReadBufPtr = _ReadBuf;
133         AH->ClosePtr = _CloseArchive;
134         AH->PrintTocDataPtr = _PrintTocData;
135         AH->ReadExtraTocPtr = _ReadExtraToc;
136         AH->WriteExtraTocPtr = _WriteExtraToc;
137         AH->PrintExtraTocPtr = _PrintExtraToc;
138
139         AH->StartBlobsPtr = _StartBlobs;
140         AH->StartBlobPtr = _StartBlob;
141         AH->EndBlobPtr = _EndBlob;
142         AH->EndBlobsPtr = _EndBlobs;
143
144         /*
145          * Set up some special context used in compressing data.
146          */
147         ctx = (lclContext *) malloc(sizeof(lclContext));
148         if (ctx == NULL)
149                 die_horribly(AH, modulename, "out of memory\n");
150         AH->formatData = (void *) ctx;
151
152         ctx->zp = (z_streamp) malloc(sizeof(z_stream));
153         if (ctx->zp == NULL)
154                 die_horribly(AH, modulename, "out of memory\n");
155
156         /* Initialize LO buffering */
157         AH->lo_buf_size = LOBBUFSIZE;
158         AH->lo_buf = (void *)malloc(LOBBUFSIZE);
159         if(AH->lo_buf == NULL)
160                 die_horribly(AH, modulename, "out of memory\n");
161
162         /*
163          * zlibOutSize is the buffer size we tell zlib it can output to.  We
164          * actually allocate one extra byte because some routines want to
165          * append a trailing zero byte to the zlib output.      The input buffer
166          * is expansible and is always of size ctx->inSize; zlibInSize is just
167          * the initial default size for it.
168          */
169         ctx->zlibOut = (char *) malloc(zlibOutSize + 1);
170         ctx->zlibIn = (char *) malloc(zlibInSize);
171         ctx->inSize = zlibInSize;
172         ctx->filePos = 0;
173
174         if (ctx->zlibOut == NULL || ctx->zlibIn == NULL)
175                 die_horribly(AH, modulename, "out of memory\n");
176
177         /*
178          * Now open the file
179          */
180         if (AH->mode == archModeWrite)
181         {
182
183                 if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
184                         AH->FH = fopen(AH->fSpec, PG_BINARY_W);
185                 else
186                         AH->FH = stdout;
187
188                 if (!AH->FH)
189                         die_horribly(AH, modulename, "could not open archive file %s: %s\n", AH->fSpec, strerror(errno));
190
191                 ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);
192
193         }
194         else
195         {
196
197                 if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
198                         AH->FH = fopen(AH->fSpec, PG_BINARY_R);
199                 else
200                         AH->FH = stdin;
201                 if (!AH->FH)
202                         die_horribly(AH, modulename, "could not open archive file %s: %s\n", AH->fSpec, strerror(errno));
203
204                 ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);
205
206                 ReadHead(AH);
207                 ReadToc(AH);
208                 ctx->dataStart = _getFilePos(AH, ctx);
209         }
210
211 }
212
213 /*
214  * Called by the Archiver when the dumper creates a new TOC entry.
215  *
216  * Optional.
217  *
218  * Set up extrac format-related TOC data.
219 */
220 static void
221 _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
222 {
223         lclTocEntry *ctx;
224
225         ctx = (lclTocEntry *) calloc(1, sizeof(lclTocEntry));
226         if (te->dataDumper)
227                 ctx->dataPos = -1;
228         else
229                 ctx->dataPos = 0;
230         ctx->dataLen = 0;
231         te->formatData = (void *) ctx;
232
233 }
234
235 /*
236  * Called by the Archiver to save any extra format-related TOC entry
237  * data.
238  *
239  * Optional.
240  *
241  * Use the Archiver routines to write data - they are non-endian, and
242  * maintain other important file information.
243  */
244 static void
245 _WriteExtraToc(ArchiveHandle *AH, TocEntry *te)
246 {
247         lclTocEntry *ctx = (lclTocEntry *) te->formatData;
248
249         WriteInt(AH, ctx->dataPos);
250         WriteInt(AH, ctx->dataLen);
251 }
252
253 /*
254  * Called by the Archiver to read any extra format-related TOC data.
255  *
256  * Optional.
257  *
258  * Needs to match the order defined in _WriteExtraToc, and sould also
259  * use the Archiver input routines.
260  */
261 static void
262 _ReadExtraToc(ArchiveHandle *AH, TocEntry *te)
263 {
264         lclTocEntry *ctx = (lclTocEntry *) te->formatData;
265
266         if (ctx == NULL)
267         {
268                 ctx = (lclTocEntry *) malloc(sizeof(lclTocEntry));
269                 te->formatData = (void *) ctx;
270         }
271
272         ctx->dataPos = ReadInt(AH);
273         ctx->dataLen = ReadInt(AH);
274 }
275
276 /*
277  * Called by the Archiver when restoring an archive to output a comment
278  * that includes useful information about the TOC entry.
279  *
280  * Optional.
281  *
282  */
283 static void
284 _PrintExtraToc(ArchiveHandle *AH, TocEntry *te)
285 {
286         lclTocEntry *ctx = (lclTocEntry *) te->formatData;
287
288         ahprintf(AH, "-- Data Pos: %d (Length %d)\n", ctx->dataPos, ctx->dataLen);
289 }
290
291 /*
292  * Called by the archiver when saving TABLE DATA (not schema). This routine
293  * should save whatever format-specific information is needed to read
294  * the archive back.
295  *
296  * It is called just prior to the dumper's 'DataDumper' routine being called.
297  *
298  * Optional, but strongly recommended.
299  *
300  */
301 static void
302 _StartData(ArchiveHandle *AH, TocEntry *te)
303 {
304         lclContext *ctx = (lclContext *) AH->formatData;
305         lclTocEntry *tctx = (lclTocEntry *) te->formatData;
306
307         tctx->dataPos = _getFilePos(AH, ctx);
308
309         _WriteByte(AH, BLK_DATA);       /* Block type */
310         WriteInt(AH, te->id);           /* For sanity check */
311
312         _StartDataCompressor(AH, te);
313
314 }
315
316 /*
317  * Called by archiver when dumper calls WriteData. This routine is
318  * called for both BLOB and TABLE data; it is the responsibility of
319  * the format to manage each kind of data using StartBlob/StartData.
320  *
321  * It should only be called from withing a DataDumper routine.
322  *
323  * Mandatory.
324  *
325  */
326 static int
327 _WriteData(ArchiveHandle *AH, const void *data, int dLen)
328 {
329         lclContext *ctx = (lclContext *) AH->formatData;
330         z_streamp       zp = ctx->zp;
331
332         zp->next_in = (void *) data;
333         zp->avail_in = dLen;
334
335         while (zp->avail_in != 0)
336         {
337                 /* printf("Deflating %d bytes\n", dLen); */
338                 _DoDeflate(AH, ctx, 0);
339         }
340         return dLen;
341 }
342
343 /*
344  * Called by the archiver when a dumper's 'DataDumper' routine has
345  * finished.
346  *
347  * Optional.
348  *
349  */
350 static void
351 _EndData(ArchiveHandle *AH, TocEntry *te)
352 {
353         lclContext *ctx = (lclContext *) AH->formatData;
354         lclTocEntry *tctx = (lclTocEntry *) te->formatData;
355
356         _EndDataCompressor(AH, te);
357
358         tctx->dataLen = _getFilePos(AH, ctx) - tctx->dataPos;
359 }
360
361 /*
362  * Called by the archiver when starting to save all BLOB DATA (not schema).
363  * This routine should save whatever format-specific information is needed
364  * to read the BLOBs back into memory.
365  *
366  * It is called just prior to the dumper's DataDumper routine.
367  *
368  * Optional, but strongly recommended.
369  *
370  */
371 static void
372 _StartBlobs(ArchiveHandle *AH, TocEntry *te)
373 {
374         lclContext *ctx = (lclContext *) AH->formatData;
375         lclTocEntry *tctx = (lclTocEntry *) te->formatData;
376
377         tctx->dataPos = _getFilePos(AH, ctx);
378
379         _WriteByte(AH, BLK_BLOBS);      /* Block type */
380         WriteInt(AH, te->id);           /* For sanity check */
381
382 }
383
384 /*
385  * Called by the archiver when the dumper calls StartBlob.
386  *
387  * Mandatory.
388  *
389  * Must save the passed OID for retrieval at restore-time.
390  */
391 static void
392 _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
393 {
394         if (oid == 0)
395                 die_horribly(AH, modulename, "invalid OID for large object\n");
396
397         WriteInt(AH, oid);
398         _StartDataCompressor(AH, te);
399 }
400
401 /*
402  * Called by the archiver when the dumper calls EndBlob.
403  *
404  * Optional.
405  *
406  */
407 static void
408 _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
409 {
410         _EndDataCompressor(AH, te);
411 }
412
413 /*
414  * Called by the archiver when finishing saving all BLOB DATA.
415  *
416  * Optional.
417  *
418  */
419 static void
420 _EndBlobs(ArchiveHandle *AH, TocEntry *te)
421 {
422         /* Write out a fake zero OID to mark end-of-blobs. */
423         WriteInt(AH, 0);
424 }
425
426 /*
427  * Print data for a gievn TOC entry
428 */
429 static void
430 _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
431 {
432         lclContext *ctx = (lclContext *) AH->formatData;
433         int                     id;
434         lclTocEntry *tctx = (lclTocEntry *) te->formatData;
435         int                     blkType;
436         int                     found = 0;
437
438         if (tctx->dataPos == 0)
439                 return;
440
441         if (!ctx->hasSeek || tctx->dataPos < 0)
442         {
443
444                 /* Skip over unnecessary blocks until we get the one we want. */
445
446                 found = 0;
447
448                 _readBlockHeader(AH, &blkType, &id);
449
450                 while (id != te->id)
451                 {
452
453                         if ((TocIDRequired(AH, id, ropt) & 2) != 0)
454                                 die_horribly(AH, modulename,
455                                                          "Dumping a specific TOC data block out of order is not supported"
456                                   " without id on this input stream (fseek required)\n");
457
458                         switch (blkType)
459                         {
460
461                                 case BLK_DATA:
462
463                                         _skipData(AH);
464                                         break;
465
466                                 case BLK_BLOBS:
467
468                                         _skipBlobs(AH);
469                                         break;
470
471                                 default:                /* Always have a default */
472
473                                         die_horribly(AH, modulename,
474                                                                  "unrecognized data block type (%d) while searching archive\n",
475                                                                  blkType);
476                                         break;
477                         }
478
479                         _readBlockHeader(AH, &blkType, &id);
480
481                 }
482
483         }
484         else
485         {
486
487                 /* Grab it */
488
489                 if (fseek(AH->FH, tctx->dataPos, SEEK_SET) != 0)
490                         die_horribly(AH, modulename, "error during file seek: %s\n", strerror(errno));
491
492                 _readBlockHeader(AH, &blkType, &id);
493
494         }
495
496         /* Are we sane? */
497         if (id != te->id)
498                 die_horribly(AH, modulename, "found unexpected block ID (%d) when reading data - expected %d\n",
499                                          id, te->id);
500
501         switch (blkType)
502         {
503
504                 case BLK_DATA:
505
506                         _PrintData(AH);
507                         break;
508
509                 case BLK_BLOBS:
510
511                         if (!AH->connection)
512                                 die_horribly(AH, modulename, "large objects cannot be loaded without a database connection\n");
513
514                         _LoadBlobs(AH);
515                         break;
516
517                 default:                                /* Always have a default */
518
519                         die_horribly(AH, modulename, "unrecognized data block type %d while restoring archive\n",
520                                                  blkType);
521                         break;
522         }
523
524         ahprintf(AH, "\n\n");
525 }
526
527 /*
528  * Print data from current file position.
529 */
530 static void
531 _PrintData(ArchiveHandle *AH)
532 {
533         lclContext *ctx = (lclContext *) AH->formatData;
534         z_streamp       zp = ctx->zp;
535         int                     blkLen;
536         char       *in = ctx->zlibIn;
537         int                     cnt;
538
539 #ifdef HAVE_LIBZ
540         int                     res;
541         char       *out = ctx->zlibOut;
542 #endif
543
544 #ifdef HAVE_LIBZ
545
546         res = Z_OK;
547
548         if (AH->compression != 0)
549         {
550                 zp->zalloc = Z_NULL;
551                 zp->zfree = Z_NULL;
552                 zp->opaque = Z_NULL;
553
554                 if (inflateInit(zp) != Z_OK)
555                         die_horribly(AH, modulename, "could not initialize compression library: %s\n", zp->msg);
556         }
557 #endif
558
559         blkLen = ReadInt(AH);
560         while (blkLen != 0)
561         {
562                 if (blkLen + 1 > ctx->inSize)
563                 {
564                         free(ctx->zlibIn);
565                         ctx->zlibIn = NULL;
566                         ctx->zlibIn = (char *) malloc(blkLen + 1);
567                         if (!ctx->zlibIn)
568                                 die_horribly(AH, modulename, "out of memory\n");
569
570                         ctx->inSize = blkLen + 1;
571                         in = ctx->zlibIn;
572                 }
573
574                 cnt = fread(in, 1, blkLen, AH->FH);
575                 if (cnt != blkLen)
576                         die_horribly(AH, modulename, "could not read data block - expected %d, got %d\n", blkLen, cnt);
577
578                 ctx->filePos += blkLen;
579
580                 zp->next_in = in;
581                 zp->avail_in = blkLen;
582
583 #ifdef HAVE_LIBZ
584
585                 if (AH->compression != 0)
586                 {
587
588                         while (zp->avail_in != 0)
589                         {
590                                 zp->next_out = out;
591                                 zp->avail_out = zlibOutSize;
592                                 res = inflate(zp, 0);
593                                 if (res != Z_OK && res != Z_STREAM_END)
594                                         die_horribly(AH, modulename, "unable to uncompress data: %s\n", zp->msg);
595
596                                 out[zlibOutSize - zp->avail_out] = '\0';
597                                 ahwrite(out, 1, zlibOutSize - zp->avail_out, AH);
598                         }
599                 }
600                 else
601                 {
602 #endif
603                         in[zp->avail_in] = '\0';
604                         ahwrite(in, 1, zp->avail_in, AH);
605                         zp->avail_in = 0;
606
607 #ifdef HAVE_LIBZ
608                 }
609 #endif
610
611                 blkLen = ReadInt(AH);
612
613         }
614
615 #ifdef HAVE_LIBZ
616         if (AH->compression != 0)
617         {
618                 zp->next_in = NULL;
619                 zp->avail_in = 0;
620                 while (res != Z_STREAM_END)
621                 {
622                         zp->next_out = out;
623                         zp->avail_out = zlibOutSize;
624                         res = inflate(zp, 0);
625                         if (res != Z_OK && res != Z_STREAM_END)
626                                 die_horribly(AH, modulename, "unable to uncompress data: %s\n", zp->msg);
627
628                         out[zlibOutSize - zp->avail_out] = '\0';
629                         ahwrite(out, 1, zlibOutSize - zp->avail_out, AH);
630                 }
631                 if (inflateEnd(zp) != Z_OK)
632                         die_horribly(AH, modulename, "could not close compression library: %s\n", zp->msg);
633         }
634 #endif
635
636 }
637
638 static void
639 _LoadBlobs(ArchiveHandle *AH)
640 {
641         int                     oid;
642
643         StartRestoreBlobs(AH);
644
645         oid = ReadInt(AH);
646         while (oid != 0)
647         {
648                 StartRestoreBlob(AH, oid);
649                 _PrintData(AH);
650                 EndRestoreBlob(AH, oid);
651                 oid = ReadInt(AH);
652         }
653
654         EndRestoreBlobs(AH);
655
656 }
657
658 /*
659  * Skip the BLOBs from the current file position.
660  * BLOBS are written sequentially as data blocks (see below).
661  * Each BLOB is preceded by it's original OID.
662  * A zero OID indicated the end of the BLOBS
663  */
664 static void
665 _skipBlobs(ArchiveHandle *AH)
666 {
667         int                     oid;
668
669         oid = ReadInt(AH);
670         while (oid != 0)
671         {
672                 _skipData(AH);
673                 oid = ReadInt(AH);
674         }
675 }
676
677 /*
678  * Skip data from current file position.
679  * Data blocks are formatted as an integer length, followed by data.
680  * A zero length denoted the end of the block.
681 */
682 static void
683 _skipData(ArchiveHandle *AH)
684 {
685         lclContext *ctx = (lclContext *) AH->formatData;
686         int                     blkLen;
687         char       *in = ctx->zlibIn;
688         int                     cnt;
689
690         blkLen = ReadInt(AH);
691         while (blkLen != 0)
692         {
693                 if (blkLen > ctx->inSize)
694                 {
695                         free(ctx->zlibIn);
696                         ctx->zlibIn = (char *) malloc(blkLen);
697                         ctx->inSize = blkLen;
698                         in = ctx->zlibIn;
699                 }
700                 cnt = fread(in, 1, blkLen, AH->FH);
701                 if (cnt != blkLen)
702                         die_horribly(AH, modulename, "could not read data block - expected %d, got %d\n", blkLen, cnt);
703
704                 ctx->filePos += blkLen;
705
706                 blkLen = ReadInt(AH);
707         }
708
709 }
710
711 /*
712  * Write a byte of data to the archive.
713  *
714  * Mandatory.
715  *
716  * Called by the archiver to do integer & byte output to the archive.
717  * These routines are only used to read & write headers & TOC.
718  *
719  */
720 static int
721 _WriteByte(ArchiveHandle *AH, const int i)
722 {
723         lclContext *ctx = (lclContext *) AH->formatData;
724         int                     res;
725
726         res = fputc(i, AH->FH);
727         if (res != EOF)
728                 ctx->filePos += 1;
729         else
730                 die_horribly(AH, modulename, "could not write byte: %s\n", strerror(errno));
731         return res;
732 }
733
734 /*
735  * Read a byte of data from the archive.
736  *
737  * Mandatory
738  *
739  * Called by the archiver to read bytes & integers from the archive.
740  * These routines are only used to read & write headers & TOC.
741  *
742  */
743 static int
744 _ReadByte(ArchiveHandle *AH)
745 {
746         lclContext *ctx = (lclContext *) AH->formatData;
747         int                     res;
748
749         res = fgetc(AH->FH);
750         if (res != EOF)
751                 ctx->filePos += 1;
752         return res;
753 }
754
755 /*
756  * Write a buffer of data to the archive.
757  *
758  * Mandatory.
759  *
760  * Called by the archiver to write a block of bytes to the archive.
761  * These routines are only used to read & write headers & TOC.
762  *
763  */
764 static int
765 _WriteBuf(ArchiveHandle *AH, const void *buf, int len)
766 {
767         lclContext *ctx = (lclContext *) AH->formatData;
768         int                     res;
769
770         res = fwrite(buf, 1, len, AH->FH);
771
772         if (res != len)
773                 die_horribly(AH, modulename, "write error in _WriteBuf (%d != %d)\n", res, len);
774
775         ctx->filePos += res;
776         return res;
777 }
778
779 /*
780  * Read a block of bytes from the archive.
781  *
782  * Mandatory.
783  *
784  * Called by the archiver to read a block of bytes from the archive
785  * These routines are only used to read & write headers & TOC.
786  *
787  */
788 static int
789 _ReadBuf(ArchiveHandle *AH, void *buf, int len)
790 {
791         lclContext *ctx = (lclContext *) AH->formatData;
792         int                     res;
793
794         res = fread(buf, 1, len, AH->FH);
795         ctx->filePos += res;
796
797         return res;
798 }
799
800 /*
801  * Close the archive.
802  *
803  * Mandatory.
804  *
805  * When writing the archive, this is the routine that actually starts
806  * the process of saving it to files. No data should be written prior
807  * to this point, since the user could sort the TOC after creating it.
808  *
809  * If an archive is to be written, this toutine must call:
810  *              WriteHead                       to save the archive header
811  *              WriteToc                        to save the TOC entries
812  *              WriteDataChunks         to save all DATA & BLOBs.
813  *
814  */
815 static void
816 _CloseArchive(ArchiveHandle *AH)
817 {
818         lclContext *ctx = (lclContext *) AH->formatData;
819         int                     tpos;
820
821         if (AH->mode == archModeWrite)
822         {
823                 WriteHead(AH);
824                 tpos = ftell(AH->FH);
825                 WriteToc(AH);
826                 ctx->dataStart = _getFilePos(AH, ctx);
827                 WriteDataChunks(AH);
828
829                 /*
830                  * This is not an essential operation - it is really only needed
831                  * if we expect to be doing seeks to read the data back - it may
832                  * be ok to just use the existing self-consistent block
833                  * formatting.
834                  */
835                 if (ctx->hasSeek)
836                 {
837                         fseek(AH->FH, tpos, SEEK_SET);
838                         WriteToc(AH);
839                 }
840         }
841
842         if (fclose(AH->FH) != 0)
843                 die_horribly(AH, modulename, "could not close archive file: %s\n", strerror(errno));
844
845         AH->FH = NULL;
846 }
847
848 /*--------------------------------------------------
849  * END OF FORMAT CALLBACKS
850  *--------------------------------------------------
851  */
852
853 /*
854  * Get the current position in the archive file.
855  */
856 static int
857 _getFilePos(ArchiveHandle *AH, lclContext *ctx)
858 {
859         int                     pos;
860
861         if (ctx->hasSeek)
862         {
863                 pos = ftell(AH->FH);
864                 if (pos != ctx->filePos)
865                 {
866                         write_msg(modulename, "WARNING: ftell mismatch with expected position -- ftell ignored\n");
867                         pos = ctx->filePos;
868                 }
869         }
870         else
871                 pos = ctx->filePos;
872         return pos;
873 }
874
875 /*
876  * Read a data block header. The format changed in V1.3, so we
877  * put the code here for simplicity.
878  */
879 static void
880 _readBlockHeader(ArchiveHandle *AH, int *type, int *id)
881 {
882         if (AH->version < K_VERS_1_3)
883                 *type = BLK_DATA;
884         else
885                 *type = _ReadByte(AH);;
886
887         *id = ReadInt(AH);
888 }
889
890 /*
891  * If zlib is available, then startit up. This is called from
892  * StartData & StartBlob. The buffers are setup in the Init routine.
893  *
894  */
895 static void
896 _StartDataCompressor(ArchiveHandle *AH, TocEntry *te)
897 {
898         lclContext *ctx = (lclContext *) AH->formatData;
899         z_streamp       zp = ctx->zp;
900
901 #ifdef HAVE_LIBZ
902
903         if (AH->compression < 0 || AH->compression > 9)
904                 AH->compression = Z_DEFAULT_COMPRESSION;
905
906         if (AH->compression != 0)
907         {
908                 zp->zalloc = Z_NULL;
909                 zp->zfree = Z_NULL;
910                 zp->opaque = Z_NULL;
911
912                 if (deflateInit(zp, AH->compression) != Z_OK)
913                         die_horribly(AH, modulename, "could not initialize compression library: %s\n", zp->msg);
914         }
915
916 #else
917
918         AH->compression = 0;
919 #endif
920
921         /* Just be paranoid - maybe End is called after Start, with no Write */
922         zp->next_out = ctx->zlibOut;
923         zp->avail_out = zlibOutSize;
924 }
925
926 /*
927  * Send compressed data to the output stream (via ahwrite).
928  * Each data chunk is preceded by it's length.
929  * In the case of Z0, or no zlib, just write the raw data.
930  *
931  */
932 static int
933 _DoDeflate(ArchiveHandle *AH, lclContext *ctx, int flush)
934 {
935         z_streamp       zp = ctx->zp;
936
937 #ifdef HAVE_LIBZ
938         char       *out = ctx->zlibOut;
939         int                     res = Z_OK;
940
941         if (AH->compression != 0)
942         {
943                 res = deflate(zp, flush);
944                 if (res == Z_STREAM_ERROR)
945                         die_horribly(AH, modulename, "could not compress data: %s\n", zp->msg);
946
947                 if (((flush == Z_FINISH) && (zp->avail_out < zlibOutSize))
948                         || (zp->avail_out == 0)
949                         || (zp->avail_in != 0)
950                         )
951                 {
952                         /*
953                          * Extra paranoia: avoid zero-length chunks since a zero
954                          * length chunk is the EOF marker. This should never happen
955                          * but...
956                          */
957                         if (zp->avail_out < zlibOutSize)
958                         {
959                                 /*
960                                  * printf("Wrote %d byte deflated chunk\n", zlibOutSize -
961                                  * zp->avail_out);
962                                  */
963                                 WriteInt(AH, zlibOutSize - zp->avail_out);
964                                 if (fwrite(out, 1, zlibOutSize - zp->avail_out, AH->FH) != (zlibOutSize - zp->avail_out))
965                                         die_horribly(AH, modulename, "could not write compressed chunk\n");
966                                 ctx->filePos += zlibOutSize - zp->avail_out;
967                         }
968                         zp->next_out = out;
969                         zp->avail_out = zlibOutSize;
970                 }
971         }
972         else
973 #endif
974         {
975                 if (zp->avail_in > 0)
976                 {
977                         WriteInt(AH, zp->avail_in);
978                         if (fwrite(zp->next_in, 1, zp->avail_in, AH->FH) != zp->avail_in)
979                                 die_horribly(AH, modulename, "could not write uncompressed chunk\n");
980                         ctx->filePos += zp->avail_in;
981                         zp->avail_in = 0;
982                 }
983                 else
984                 {
985 #ifdef HAVE_LIBZ
986                         if (flush == Z_FINISH)
987                                 res = Z_STREAM_END;
988 #endif
989                 }
990
991
992         }
993
994 #ifdef HAVE_LIBZ
995         return res;
996 #else
997         return 1;
998 #endif
999
1000 }
1001
1002 /*
1003  * Terminate zlib context and flush it's buffers. If no zlib
1004  * then just return.
1005  *
1006  */
1007 static void
1008 _EndDataCompressor(ArchiveHandle *AH, TocEntry *te)
1009 {
1010
1011 #ifdef HAVE_LIBZ
1012         lclContext *ctx = (lclContext *) AH->formatData;
1013         z_streamp       zp = ctx->zp;
1014         int                     res;
1015
1016         if (AH->compression != 0)
1017         {
1018                 zp->next_in = NULL;
1019                 zp->avail_in = 0;
1020
1021                 do
1022                 {
1023                         /* printf("Ending data output\n"); */
1024                         res = _DoDeflate(AH, ctx, Z_FINISH);
1025                 } while (res != Z_STREAM_END);
1026
1027                 if (deflateEnd(zp) != Z_OK)
1028                         die_horribly(AH, modulename, "could not close compression stream: %s\n", zp->msg);
1029         }
1030 #endif
1031
1032         /* Send the end marker */
1033         WriteInt(AH, 0);
1034 }