]> granicus.if.org Git - postgresql/blobdiff - src/bin/pg_dump/pg_backup_custom.c
Modify pg_dump to use error-free memory allocation macros. This avoids
[postgresql] / src / bin / pg_dump / pg_backup_custom.c
index 3edbb751f9a0a80ada721fdc9e9012610083fd64..bfdf482a6b268a2cdfbdd88009c2c0aadb736348 100644 (file)
-/*-------------------------------------------------------------------------\r
- *\r
- * pg_backup_custom.c\r
- *\r
- *     Implements the custom output format.\r
- *\r
- *     See the headers to pg_restore for more details.\r
- *\r
- * Copyright (c) 2000, Philip Warner\r
- *      Rights are granted to use this software in any way so long\r
- *      as this notice is not removed.\r
- *\r
- *     The author is not responsible for loss or damages that may\r
- *     result from it's use.\r
- *\r
- *\r
- * IDENTIFICATION\r
- *\r
- * Modifications - 28-Jun-2000 - pjw@rhyme.com.au\r
- *\r
- *     Initial version. \r
- *\r
- *-------------------------------------------------------------------------\r
- */\r
-\r
-#include <stdlib.h>\r
-#include "pg_backup.h"\r
-#include "pg_backup_archiver.h"\r
-\r
-extern int     errno;\r
-\r
-static void     _ArchiveEntry(ArchiveHandle* AH, TocEntry* te);\r
-static void    _StartData(ArchiveHandle* AH, TocEntry* te);\r
-static int     _WriteData(ArchiveHandle* AH, const void* data, int dLen);\r
-static void     _EndData(ArchiveHandle* AH, TocEntry* te);\r
-static int      _WriteByte(ArchiveHandle* AH, const int i);\r
-static int      _ReadByte(ArchiveHandle* );\r
-static int      _WriteBuf(ArchiveHandle* AH, const void* buf, int len);\r
-static int     _ReadBuf(ArchiveHandle* AH, void* buf, int len);\r
-static void     _CloseArchive(ArchiveHandle* AH);\r
-static void    _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt);\r
-static void    _WriteExtraToc(ArchiveHandle* AH, TocEntry* te);\r
-static void    _ReadExtraToc(ArchiveHandle* AH, TocEntry* te);\r
-static void    _PrintExtraToc(ArchiveHandle* AH, TocEntry* te);\r
-\r
-static void    _PrintData(ArchiveHandle* AH);\r
-static void     _skipData(ArchiveHandle* AH);\r
-\r
-#define zlibOutSize    4096\r
-#define zlibInSize     4096\r
-\r
-typedef struct {\r
-    z_streamp  zp;\r
-    char*      zlibOut;\r
-    char*      zlibIn;\r
-    int                inSize;\r
-    int                hasSeek;\r
-    int                filePos;\r
-    int                dataStart;\r
-} lclContext;\r
-\r
-typedef struct {\r
-    int                dataPos;\r
-    int                dataLen;\r
-} lclTocEntry;\r
-\r
-static int     _getFilePos(ArchiveHandle* AH, lclContext* ctx);\r
-\r
-static char* progname = "Archiver(custom)";\r
-\r
-/*\r
- *  Handler functions. \r
- */\r
-void InitArchiveFmt_Custom(ArchiveHandle* AH) \r
-{\r
-    lclContext*                ctx;\r
-\r
-    /* Assuming static functions, this can be copied for each format. */\r
-    AH->ArchiveEntryPtr = _ArchiveEntry;\r
-    AH->StartDataPtr = _StartData;\r
-    AH->WriteDataPtr = _WriteData;\r
-    AH->EndDataPtr = _EndData;\r
-    AH->WriteBytePtr = _WriteByte;\r
-    AH->ReadBytePtr = _ReadByte;\r
-    AH->WriteBufPtr = _WriteBuf;\r
-    AH->ReadBufPtr = _ReadBuf;\r
-    AH->ClosePtr = _CloseArchive;\r
-    AH->PrintTocDataPtr = _PrintTocData;\r
-    AH->ReadExtraTocPtr = _ReadExtraToc;\r
-    AH->WriteExtraTocPtr = _WriteExtraToc;\r
-    AH->PrintExtraTocPtr = _PrintExtraToc;\r
-\r
-    /*\r
-     * Set up some special context used in compressing data.\r
-    */\r
-    ctx = (lclContext*)malloc(sizeof(lclContext));\r
-    if (ctx == NULL)\r
-       die_horribly("%s: Unable to allocate archive context",progname);\r
-    AH->formatData = (void*)ctx;\r
-\r
-    ctx->zp = (z_streamp)malloc(sizeof(z_stream));\r
-    if (ctx->zp == NULL)\r
-       die_horribly("%s: unable to allocate zlib stream archive context",progname);\r
-\r
-    ctx->zlibOut = (char*)malloc(zlibOutSize);\r
-    ctx->zlibIn = (char*)malloc(zlibInSize);\r
-    ctx->inSize = zlibInSize;\r
-    ctx->filePos = 0;\r
-\r
-    if (ctx->zlibOut == NULL || ctx->zlibIn == NULL)\r
-       die_horribly("%s: unable to allocate buffers in archive context",progname);\r
-\r
-    /*\r
-     * Now open the file\r
-    */\r
-    if (AH->mode == archModeWrite) {\r
-       if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {\r
-           AH->FH = fopen(AH->fSpec, PG_BINARY_W);\r
-       } else {\r
-           AH->FH = stdout;\r
-       }\r
-\r
-       if (!AH)\r
-           die_horribly("%s: unable to open archive file %s",progname, AH->fSpec);\r
-\r
-       ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);\r
-\r
-    } else {\r
-       if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {\r
-           AH->FH = fopen(AH->fSpec, PG_BINARY_R);\r
-       } else {\r
-           AH->FH = stdin;\r
-       }\r
-       if (!AH)\r
-           die_horribly("%s: unable to open archive file %s",progname, AH->fSpec);\r
-\r
-       ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);\r
-\r
-       ReadHead(AH);\r
-       ReadToc(AH);\r
-       ctx->dataStart = _getFilePos(AH, ctx);\r
-    }\r
-\r
-}\r
-\r
-/*\r
- * - Start a new TOC entry\r
-*/\r
-static void    _ArchiveEntry(ArchiveHandle* AH, TocEntry* te) \r
-{\r
-    lclTocEntry*       ctx;\r
-\r
-    ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));\r
-    if (te->dataDumper) {\r
-       ctx->dataPos = -1;\r
-    } else {\r
-       ctx->dataPos = 0;\r
-    }\r
-    ctx->dataLen = 0;\r
-    te->formatData = (void*)ctx;\r
-\r
-}\r
-\r
-static void    _WriteExtraToc(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclTocEntry*       ctx = (lclTocEntry*)te->formatData;\r
-\r
-    WriteInt(AH, ctx->dataPos);\r
-    WriteInt(AH, ctx->dataLen);\r
-}\r
-\r
-static void    _ReadExtraToc(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclTocEntry*       ctx = (lclTocEntry*)te->formatData;\r
-\r
-    if (ctx == NULL) {\r
-       ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));\r
-       te->formatData = (void*)ctx;\r
-    }\r
-\r
-    ctx->dataPos = ReadInt( AH );\r
-    ctx->dataLen = ReadInt( AH );\r
-    \r
-}\r
-\r
-static void    _PrintExtraToc(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclTocEntry*       ctx = (lclTocEntry*)te->formatData;\r
-\r
-    ahprintf(AH, "-- Data Pos: %d (Length %d)\n", ctx->dataPos, ctx->dataLen);\r
-}\r
-\r
-static void    _StartData(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclContext*                ctx = (lclContext*)AH->formatData;\r
-    z_streamp          zp = ctx->zp;\r
-    lclTocEntry*       tctx = (lclTocEntry*)te->formatData;\r
-\r
-    tctx->dataPos = _getFilePos(AH, ctx);\r
-\r
-    WriteInt(AH, te->id); /* For sanity check */\r
-\r
-#ifdef HAVE_LIBZ\r
-\r
-    if (AH->compression < 0 || AH->compression > 9) {\r
-       AH->compression = Z_DEFAULT_COMPRESSION;\r
-    }\r
-\r
-    if (AH->compression != 0) {\r
-       zp->zalloc = Z_NULL;\r
-       zp->zfree = Z_NULL;\r
-       zp->opaque = Z_NULL;\r
-\r
-       if (deflateInit(zp, AH->compression) != Z_OK)\r
-           die_horribly("%s: could not initialize compression library - %s\n",progname, zp->msg);\r
-    }\r
-\r
-#else\r
-\r
-    AH->compression = 0;\r
-\r
-#endif\r
-\r
-    /* Just be paranoid - maye End is called after Start, with no Write */\r
-    zp->next_out = ctx->zlibOut;\r
-    zp->avail_out = zlibOutSize;\r
-}\r
-\r
-static int     _DoDeflate(ArchiveHandle* AH, lclContext* ctx, int flush) \r
-{\r
-    z_streamp   zp = ctx->zp;\r
-\r
-#ifdef HAVE_LIBZ\r
-    char*      out = ctx->zlibOut;\r
-    int                res = Z_OK;\r
-\r
-    if (AH->compression != 0) \r
-    {\r
-       res = deflate(zp, flush);\r
-       if (res == Z_STREAM_ERROR)\r
-           die_horribly("%s: could not compress data - %s\n",progname, zp->msg);\r
-\r
-       if      (      ( (flush == Z_FINISH) && (zp->avail_out < zlibOutSize) )\r
-               || (zp->avail_out == 0) \r
-               || (zp->avail_in != 0)\r
-           ) \r
-       {\r
-           /*\r
-            * Extra paranoia: avoid zero-length chunks since a zero \r
-            * length chunk is the EOF marker. This should never happen\r
-            * but...\r
-           */\r
-           if (zp->avail_out < zlibOutSize) {\r
-               /* printf("Wrote %d byte deflated chunk\n", zlibOutSize - zp->avail_out); */\r
-               WriteInt(AH, zlibOutSize - zp->avail_out);\r
-               fwrite(out, 1, zlibOutSize - zp->avail_out, AH->FH);\r
-               ctx->filePos += zlibOutSize - zp->avail_out;\r
-           }\r
-           zp->next_out = out;\r
-           zp->avail_out = zlibOutSize;\r
-       }\r
-    } else {\r
-#endif\r
-       if (zp->avail_in > 0)\r
-       {\r
-           WriteInt(AH, zp->avail_in);\r
-           fwrite(zp->next_in, 1, zp->avail_in, AH->FH);\r
-           ctx->filePos += zp->avail_in;\r
-           zp->avail_in = 0;\r
-       } else {\r
-#ifdef HAVE_LIBZ\r
-           if (flush == Z_FINISH)\r
-               res = Z_STREAM_END;\r
-#endif\r
-       }\r
-\r
-\r
-#ifdef HAVE_LIBZ\r
-    }\r
-\r
-    return res;\r
-#else\r
-    return 1;\r
-#endif\r
-\r
-}\r
-\r
-static int     _WriteData(ArchiveHandle* AH, const void* data, int dLen)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    z_streamp  zp = ctx->zp;\r
-\r
-    zp->next_in = (void*)data;\r
-    zp->avail_in = dLen;\r
-\r
-    while (zp->avail_in != 0) {\r
-       /* printf("Deflating %d bytes\n", dLen); */\r
-       _DoDeflate(AH, ctx, 0);\r
-    }\r
-    return dLen;\r
-}\r
-\r
-static void    _EndData(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclContext*                ctx = (lclContext*)AH->formatData;\r
-    lclTocEntry*       tctx = (lclTocEntry*) te->formatData;\r
-\r
-#ifdef HAVE_LIBZ\r
-    z_streamp          zp = ctx->zp;\r
-    int                        res;\r
-\r
-    if (AH->compression != 0)\r
-    {\r
-       zp->next_in = NULL;\r
-       zp->avail_in = 0;\r
-\r
-       do {    \r
-           /* printf("Ending data output\n"); */\r
-           res = _DoDeflate(AH, ctx, Z_FINISH);\r
-       } while (res != Z_STREAM_END);\r
-\r
-       if (deflateEnd(zp) != Z_OK)\r
-           die_horribly("%s: error closing compression stream - %s\n", progname, zp->msg);\r
-    }\r
-#endif\r
-\r
-    /* Send the end marker */\r
-    WriteInt(AH, 0);\r
-\r
-    tctx->dataLen = _getFilePos(AH, ctx) - tctx->dataPos;\r
-\r
-}\r
-\r
-/*\r
- * Print data for a gievn TOC entry\r
-*/\r
-static void    _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    int                        id;\r
-    lclTocEntry*       tctx = (lclTocEntry*) te->formatData;\r
-\r
-    if (tctx->dataPos == 0) \r
-       return;\r
-\r
-    if (!ctx->hasSeek || tctx->dataPos < 0) {\r
-       id = ReadInt(AH);\r
-\r
-       while (id != te->id) {\r
-           if (TocIDRequired(AH, id, ropt) & 2)\r
-               die_horribly("%s: Dumping a specific TOC data block out of order is not supported"\r
-                              " without on this input stream (fseek required)\n", progname);\r
-           _skipData(AH);\r
-           id = ReadInt(AH);\r
-       }\r
-    } else {\r
-\r
-       if (fseek(AH->FH, tctx->dataPos, SEEK_SET) != 0)\r
-           die_horribly("%s: error %d in file seek\n",progname, errno);\r
-\r
-       id = ReadInt(AH);\r
-\r
-    }\r
-\r
-    if (id != te->id)\r
-       die_horribly("%s: Found unexpected block ID (%d) when reading data - expected %d\n",\r
-                       progname, id, te->id);\r
-\r
-    ahprintf(AH, "--\n-- Data for TOC Entry ID %d (OID %s) %s %s\n--\n\n",\r
-               te->id, te->oid, te->desc, te->name);\r
-\r
-    _PrintData(AH);\r
-\r
-    ahprintf(AH, "\n\n");\r
-}\r
-\r
-/*\r
- * Print data from current file position.\r
-*/\r
-static void    _PrintData(ArchiveHandle* AH)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    z_streamp  zp = ctx->zp;\r
-    int                blkLen;\r
-    char*      in = ctx->zlibIn;\r
-    int                cnt;\r
-\r
-#ifdef HAVE_LIBZ\r
-\r
-    int                res;\r
-    char*      out = ctx->zlibOut;\r
-\r
-    res = Z_OK;\r
-\r
-    if (AH->compression != 0) {\r
-       zp->zalloc = Z_NULL;\r
-       zp->zfree = Z_NULL;\r
-       zp->opaque = Z_NULL;\r
-\r
-       if (inflateInit(zp) != Z_OK)\r
-           die_horribly("%s: could not initialize compression library - %s\n", progname, zp->msg);\r
-    }\r
-\r
-#endif\r
-\r
-    blkLen = ReadInt(AH);\r
-    while (blkLen != 0) {\r
-       if (blkLen > ctx->inSize) {\r
-           free(ctx->zlibIn);\r
-           ctx->zlibIn = NULL;\r
-           ctx->zlibIn = (char*)malloc(blkLen);\r
-           if (!ctx->zlibIn)\r
-               die_horribly("%s: failed to allocate decompression buffer\n", progname);\r
-\r
-           ctx->inSize = blkLen;\r
-           in = ctx->zlibIn;\r
-       }\r
-       cnt = fread(in, 1, blkLen, AH->FH);\r
-       if (cnt != blkLen) \r
-           die_horribly("%s: could not read data block - expected %d, got %d\n", progname, blkLen, cnt);\r
-\r
-       ctx->filePos += blkLen;\r
-\r
-       zp->next_in = in;\r
-       zp->avail_in = blkLen;\r
-\r
-#ifdef HAVE_LIBZ\r
-\r
-       if (AH->compression != 0) {\r
-\r
-           while (zp->avail_in != 0) {\r
-               zp->next_out = out;\r
-               zp->avail_out = zlibOutSize;\r
-               res = inflate(zp, 0);\r
-               if (res != Z_OK && res != Z_STREAM_END)\r
-                   die_horribly("%s: unable to uncompress data - %s\n", progname, zp->msg);\r
-\r
-               out[zlibOutSize - zp->avail_out] = '\0';\r
-               ahwrite(out, 1, zlibOutSize - zp->avail_out, AH);\r
-           }\r
-       } else {\r
-#endif\r
-           ahwrite(in, 1, zp->avail_in, AH);\r
-           zp->avail_in = 0;\r
-\r
-#ifdef HAVE_LIBZ\r
-       }\r
-#endif\r
-\r
-       blkLen = ReadInt(AH);\r
-    }\r
-\r
-#ifdef HAVE_LIBZ\r
-    if (AH->compression != 0) \r
-    {\r
-       zp->next_in = NULL;\r
-       zp->avail_in = 0;\r
-       while (res != Z_STREAM_END) {\r
-           zp->next_out = out;\r
-           zp->avail_out = zlibOutSize;\r
-           res = inflate(zp, 0);\r
-           if (res != Z_OK && res != Z_STREAM_END)\r
-               die_horribly("%s: unable to uncompress data - %s\n", progname, zp->msg);\r
-\r
-           out[zlibOutSize - zp->avail_out] = '\0';\r
-           ahwrite(out, 1, zlibOutSize - zp->avail_out, AH);\r
-       }\r
-    }\r
-#endif\r
-\r
-}\r
-\r
-/*\r
- * Skip data from current file position.\r
-*/\r
-static void    _skipData(ArchiveHandle* AH)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    int                blkLen;\r
-    char*      in = ctx->zlibIn;\r
-    int                cnt;\r
-\r
-    blkLen = ReadInt(AH);\r
-    while (blkLen != 0) {\r
-       if (blkLen > ctx->inSize) {\r
-           free(ctx->zlibIn);\r
-           ctx->zlibIn = (char*)malloc(blkLen);\r
-           ctx->inSize = blkLen;\r
-           in = ctx->zlibIn;\r
-       }\r
-       cnt = fread(in, 1, blkLen, AH->FH);\r
-       if (cnt != blkLen) \r
-           die_horribly("%s: could not read data block - expected %d, got %d\n", progname, blkLen, cnt);\r
-\r
-       ctx->filePos += blkLen;\r
-\r
-       blkLen = ReadInt(AH);\r
-    }\r
-\r
-}\r
-\r
-static int     _WriteByte(ArchiveHandle* AH, const int i)\r
-{\r
-    lclContext*                ctx = (lclContext*)AH->formatData;\r
-    int                        res;\r
-\r
-    res = fputc(i, AH->FH);\r
-    if (res != EOF) {\r
-       ctx->filePos += 1;\r
-    }\r
-    return res;\r
-}\r
-\r
-static int     _ReadByte(ArchiveHandle* AH)\r
-{\r
-    lclContext*                ctx = (lclContext*)AH->formatData;\r
-    int                        res;\r
-\r
-    res = fgetc(AH->FH);\r
-    if (res != EOF) {\r
-       ctx->filePos += 1;\r
-    }\r
-    return res;\r
-}\r
-\r
-static int     _WriteBuf(ArchiveHandle* AH, const void* buf, int len)\r
-{\r
-    lclContext*                ctx = (lclContext*)AH->formatData;\r
-    int                        res;\r
-    res = fwrite(buf, 1, len, AH->FH);\r
-    ctx->filePos += res;\r
-    return res;\r
-}\r
-\r
-static int     _ReadBuf(ArchiveHandle* AH, void* buf, int len)\r
-{\r
-    lclContext*                ctx = (lclContext*)AH->formatData;\r
-    int                        res;\r
-    res = fread(buf, 1, len, AH->FH);\r
-    ctx->filePos += res;\r
-    return res;\r
-}\r
-\r
-static void    _CloseArchive(ArchiveHandle* AH)\r
-{\r
-    lclContext*                ctx = (lclContext*)AH->formatData;\r
-    int                        tpos;\r
-\r
-    if (AH->mode == archModeWrite) {\r
-       WriteHead(AH);\r
-       tpos = ftell(AH->FH);\r
-       WriteToc(AH);\r
-       ctx->dataStart = _getFilePos(AH, ctx);\r
-       WriteDataChunks(AH);\r
-       /* This is not an essential operation - it is really only\r
-        * needed if we expect to be doing seeks to read the data back\r
-        * - it may be ok to just use the existing self-consistent block\r
-        * formatting.\r
-        */\r
-       if (ctx->hasSeek) {\r
-           fseek(AH->FH, tpos, SEEK_SET);\r
-           WriteToc(AH);\r
-       }\r
-    }\r
-\r
-    fclose(AH->FH);\r
-    AH->FH = NULL; \r
-}\r
-\r
-static int     _getFilePos(ArchiveHandle* AH, lclContext* ctx) \r
-{\r
-    int                pos;\r
-    if (ctx->hasSeek) {\r
-       pos = ftell(AH->FH);\r
-       if (pos != ctx->filePos) {\r
-           fprintf(stderr, "Warning: ftell mismatch with filePos\n");\r
-       }\r
-    } else {\r
-       pos = ctx->filePos;\r
-    }\r
-    return pos;\r
-}\r
-\r
-\r
+/*-------------------------------------------------------------------------
+ *
+ * pg_backup_custom.c
+ *
+ *     Implements the custom output format.
+ *
+ *     The comments with the routined in this code are a good place to
+ *     understand how to write a new format.
+ *
+ *     See the headers to pg_restore for more details.
+ *
+ * Copyright (c) 2000, Philip Warner
+ *             Rights are granted to use this software in any way so long
+ *             as this notice is not removed.
+ *
+ *     The author is not responsible for loss or damages that may
+ *     and any liability will be limited to the time taken to fix any
+ *     related bug.
+ *
+ *
+ * IDENTIFICATION
+ *             src/bin/pg_dump/pg_backup_custom.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "compress_io.h"
+#include "common.h"
+
+/*--------
+ * Routines in the format interface
+ *--------
+ */
+
+static void _ArchiveEntry(ArchiveHandle *AH, TocEntry *te);
+static void _StartData(ArchiveHandle *AH, TocEntry *te);
+static size_t _WriteData(ArchiveHandle *AH, const void *data, size_t dLen);
+static void _EndData(ArchiveHandle *AH, TocEntry *te);
+static int     _WriteByte(ArchiveHandle *AH, const int i);
+static int     _ReadByte(ArchiveHandle *);
+static size_t _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len);
+static size_t _ReadBuf(ArchiveHandle *AH, void *buf, size_t len);
+static void _CloseArchive(ArchiveHandle *AH);
+static void _ReopenArchive(ArchiveHandle *AH);
+static void _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
+static void _WriteExtraToc(ArchiveHandle *AH, TocEntry *te);
+static void _ReadExtraToc(ArchiveHandle *AH, TocEntry *te);
+static void _PrintExtraToc(ArchiveHandle *AH, TocEntry *te);
+
+static void _PrintData(ArchiveHandle *AH);
+static void _skipData(ArchiveHandle *AH);
+static void _skipBlobs(ArchiveHandle *AH);
+
+static void _StartBlobs(ArchiveHandle *AH, TocEntry *te);
+static void _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
+static void _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
+static void _EndBlobs(ArchiveHandle *AH, TocEntry *te);
+static void _LoadBlobs(ArchiveHandle *AH, bool drop);
+static void _Clone(ArchiveHandle *AH);
+static void _DeClone(ArchiveHandle *AH);
+
+typedef struct
+{
+       CompressorState *cs;
+       int                     hasSeek;
+       pgoff_t         filePos;
+       pgoff_t         dataStart;
+} lclContext;
+
+typedef struct
+{
+       int                     dataState;
+       pgoff_t         dataPos;
+} lclTocEntry;
+
+
+/*------
+ * Static declarations
+ *------
+ */
+static void _readBlockHeader(ArchiveHandle *AH, int *type, int *id);
+static pgoff_t _getFilePos(ArchiveHandle *AH, lclContext *ctx);
+
+static size_t _CustomWriteFunc(ArchiveHandle *AH, const char *buf, size_t len);
+static size_t _CustomReadFunc(ArchiveHandle *AH, char **buf, size_t *buflen);
+
+static const char *modulename = gettext_noop("custom archiver");
+
+
+
+/*
+ *     Init routine required by ALL formats. This is a global routine
+ *     and should be declared in pg_backup_archiver.h
+ *
+ *     It's task is to create any extra archive context (using AH->formatData),
+ *     and to initialize the supported function pointers.
+ *
+ *     It should also prepare whatever it's input source is for reading/writing,
+ *     and in the case of a read mode connection, it should load the Header & TOC.
+ */
+void
+InitArchiveFmt_Custom(ArchiveHandle *AH)
+{
+       lclContext *ctx;
+
+       /* Assuming static functions, this can be copied for each format. */
+       AH->ArchiveEntryPtr = _ArchiveEntry;
+       AH->StartDataPtr = _StartData;
+       AH->WriteDataPtr = _WriteData;
+       AH->EndDataPtr = _EndData;
+       AH->WriteBytePtr = _WriteByte;
+       AH->ReadBytePtr = _ReadByte;
+       AH->WriteBufPtr = _WriteBuf;
+       AH->ReadBufPtr = _ReadBuf;
+       AH->ClosePtr = _CloseArchive;
+       AH->ReopenPtr = _ReopenArchive;
+       AH->PrintTocDataPtr = _PrintTocData;
+       AH->ReadExtraTocPtr = _ReadExtraToc;
+       AH->WriteExtraTocPtr = _WriteExtraToc;
+       AH->PrintExtraTocPtr = _PrintExtraToc;
+
+       AH->StartBlobsPtr = _StartBlobs;
+       AH->StartBlobPtr = _StartBlob;
+       AH->EndBlobPtr = _EndBlob;
+       AH->EndBlobsPtr = _EndBlobs;
+       AH->ClonePtr = _Clone;
+       AH->DeClonePtr = _DeClone;
+
+       /* Set up a private area. */
+       ctx = (lclContext *) pg_calloc(1, sizeof(lclContext));
+       AH->formatData = (void *) ctx;
+
+       /* Initialize LO buffering */
+       AH->lo_buf_size = LOBBUFSIZE;
+       AH->lo_buf = (void *) pg_malloc(LOBBUFSIZE);
+
+       ctx->filePos = 0;
+
+       /*
+        * Now open the file
+        */
+       if (AH->mode == archModeWrite)
+       {
+               if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
+               {
+                       AH->FH = fopen(AH->fSpec, PG_BINARY_W);
+                       if (!AH->FH)
+                               die_horribly(AH, modulename, "could not open output file \"%s\": %s\n",
+                                                        AH->fSpec, strerror(errno));
+               }
+               else
+               {
+                       AH->FH = stdout;
+                       if (!AH->FH)
+                               die_horribly(AH, modulename, "could not open output file: %s\n",
+                                                        strerror(errno));
+               }
+
+               ctx->hasSeek = checkSeek(AH->FH);
+       }
+       else
+       {
+               if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
+               {
+                       AH->FH = fopen(AH->fSpec, PG_BINARY_R);
+                       if (!AH->FH)
+                               die_horribly(AH, modulename, "could not open input file \"%s\": %s\n",
+                                                        AH->fSpec, strerror(errno));
+               }
+               else
+               {
+                       AH->FH = stdin;
+                       if (!AH->FH)
+                               die_horribly(AH, modulename, "could not open input file: %s\n",
+                                                        strerror(errno));
+               }
+
+               ctx->hasSeek = checkSeek(AH->FH);
+
+               ReadHead(AH);
+               ReadToc(AH);
+               ctx->dataStart = _getFilePos(AH, ctx);
+       }
+
+}
+
+/*
+ * Called by the Archiver when the dumper creates a new TOC entry.
+ *
+ * Optional.
+ *
+ * Set up extrac format-related TOC data.
+*/
+static void
+_ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
+{
+       lclTocEntry *ctx;
+
+       ctx = (lclTocEntry *) pg_calloc(1, sizeof(lclTocEntry));
+       if (te->dataDumper)
+               ctx->dataState = K_OFFSET_POS_NOT_SET;
+       else
+               ctx->dataState = K_OFFSET_NO_DATA;
+
+       te->formatData = (void *) ctx;
+}
+
+/*
+ * Called by the Archiver to save any extra format-related TOC entry
+ * data.
+ *
+ * Optional.
+ *
+ * Use the Archiver routines to write data - they are non-endian, and
+ * maintain other important file information.
+ */
+static void
+_WriteExtraToc(ArchiveHandle *AH, TocEntry *te)
+{
+       lclTocEntry *ctx = (lclTocEntry *) te->formatData;
+
+       WriteOffset(AH, ctx->dataPos, ctx->dataState);
+}
+
+/*
+ * Called by the Archiver to read any extra format-related TOC data.
+ *
+ * Optional.
+ *
+ * Needs to match the order defined in _WriteExtraToc, and sould also
+ * use the Archiver input routines.
+ */
+static void
+_ReadExtraToc(ArchiveHandle *AH, TocEntry *te)
+{
+       lclTocEntry *ctx = (lclTocEntry *) te->formatData;
+
+       if (ctx == NULL)
+       {
+               ctx = (lclTocEntry *) pg_calloc(1, sizeof(lclTocEntry));
+               te->formatData = (void *) ctx;
+       }
+
+       ctx->dataState = ReadOffset(AH, &(ctx->dataPos));
+
+       /*
+        * Prior to V1.7 (pg7.3), we dumped the data size as an int now we don't
+        * dump it at all.
+        */
+       if (AH->version < K_VERS_1_7)
+               ReadInt(AH);
+}
+
+/*
+ * Called by the Archiver when restoring an archive to output a comment
+ * that includes useful information about the TOC entry.
+ *
+ * Optional.
+ *
+ */
+static void
+_PrintExtraToc(ArchiveHandle *AH, TocEntry *te)
+{
+       lclTocEntry *ctx = (lclTocEntry *) te->formatData;
+
+       if (AH->public.verbose)
+               ahprintf(AH, "-- Data Pos: " INT64_FORMAT "\n",
+                                (int64) ctx->dataPos);
+}
+
+/*
+ * Called by the archiver when saving TABLE DATA (not schema). This routine
+ * should save whatever format-specific information is needed to read
+ * the archive back.
+ *
+ * It is called just prior to the dumper's 'DataDumper' routine being called.
+ *
+ * Optional, but strongly recommended.
+ *
+ */
+static void
+_StartData(ArchiveHandle *AH, TocEntry *te)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+       lclTocEntry *tctx = (lclTocEntry *) te->formatData;
+
+       tctx->dataPos = _getFilePos(AH, ctx);
+       tctx->dataState = K_OFFSET_POS_SET;
+
+       _WriteByte(AH, BLK_DATA);       /* Block type */
+       WriteInt(AH, te->dumpId);       /* For sanity check */
+
+       ctx->cs = AllocateCompressor(AH->compression, _CustomWriteFunc);
+}
+
+/*
+ * Called by archiver when dumper calls WriteData. This routine is
+ * called for both BLOB and TABLE data; it is the responsibility of
+ * the format to manage each kind of data using StartBlob/StartData.
+ *
+ * It should only be called from within a DataDumper routine.
+ *
+ * Mandatory.
+ */
+static size_t
+_WriteData(ArchiveHandle *AH, const void *data, size_t dLen)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+       CompressorState *cs = ctx->cs;
+
+       if (dLen == 0)
+               return 0;
+
+       return WriteDataToArchive(AH, cs, data, dLen);
+}
+
+/*
+ * Called by the archiver when a dumper's 'DataDumper' routine has
+ * finished.
+ *
+ * Optional.
+ *
+ */
+static void
+_EndData(ArchiveHandle *AH, TocEntry *te)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+
+       EndCompressor(AH, ctx->cs);
+       /* Send the end marker */
+       WriteInt(AH, 0);
+}
+
+/*
+ * Called by the archiver when starting to save all BLOB DATA (not schema).
+ * This routine should save whatever format-specific information is needed
+ * to read the BLOBs back into memory.
+ *
+ * It is called just prior to the dumper's DataDumper routine.
+ *
+ * Optional, but strongly recommended.
+ */
+static void
+_StartBlobs(ArchiveHandle *AH, TocEntry *te)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+       lclTocEntry *tctx = (lclTocEntry *) te->formatData;
+
+       tctx->dataPos = _getFilePos(AH, ctx);
+       tctx->dataState = K_OFFSET_POS_SET;
+
+       _WriteByte(AH, BLK_BLOBS);      /* Block type */
+       WriteInt(AH, te->dumpId);       /* For sanity check */
+}
+
+/*
+ * Called by the archiver when the dumper calls StartBlob.
+ *
+ * Mandatory.
+ *
+ * Must save the passed OID for retrieval at restore-time.
+ */
+static void
+_StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+
+       if (oid == 0)
+               die_horribly(AH, modulename, "invalid OID for large object\n");
+
+       WriteInt(AH, oid);
+
+       ctx->cs = AllocateCompressor(AH->compression, _CustomWriteFunc);
+}
+
+/*
+ * Called by the archiver when the dumper calls EndBlob.
+ *
+ * Optional.
+ */
+static void
+_EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+
+       EndCompressor(AH, ctx->cs);
+       /* Send the end marker */
+       WriteInt(AH, 0);
+}
+
+/*
+ * Called by the archiver when finishing saving all BLOB DATA.
+ *
+ * Optional.
+ */
+static void
+_EndBlobs(ArchiveHandle *AH, TocEntry *te)
+{
+       /* Write out a fake zero OID to mark end-of-blobs. */
+       WriteInt(AH, 0);
+}
+
+/*
+ * Print data for a given TOC entry
+ */
+static void
+_PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+       lclTocEntry *tctx = (lclTocEntry *) te->formatData;
+       int                     blkType;
+       int                     id;
+
+       if (tctx->dataState == K_OFFSET_NO_DATA)
+               return;
+
+       if (!ctx->hasSeek || tctx->dataState == K_OFFSET_POS_NOT_SET)
+       {
+               /*
+                * We cannot seek directly to the desired block.  Instead, skip over
+                * block headers until we find the one we want.  This could fail if we
+                * are asked to restore items out-of-order.
+                */
+               _readBlockHeader(AH, &blkType, &id);
+
+               while (blkType != EOF && id != te->dumpId)
+               {
+                       switch (blkType)
+                       {
+                               case BLK_DATA:
+                                       _skipData(AH);
+                                       break;
+
+                               case BLK_BLOBS:
+                                       _skipBlobs(AH);
+                                       break;
+
+                               default:                /* Always have a default */
+                                       die_horribly(AH, modulename,
+                                                                "unrecognized data block type (%d) while searching archive\n",
+                                                                blkType);
+                                       break;
+                       }
+                       _readBlockHeader(AH, &blkType, &id);
+               }
+       }
+       else
+       {
+               /* We can just seek to the place we need to be. */
+               if (fseeko(AH->FH, tctx->dataPos, SEEK_SET) != 0)
+                       die_horribly(AH, modulename, "error during file seek: %s\n",
+                                                strerror(errno));
+
+               _readBlockHeader(AH, &blkType, &id);
+       }
+
+       /* Produce suitable failure message if we fell off end of file */
+       if (blkType == EOF)
+       {
+               if (tctx->dataState == K_OFFSET_POS_NOT_SET)
+                       die_horribly(AH, modulename, "could not find block ID %d in archive -- "
+                                                "possibly due to out-of-order restore request, "
+                                                "which cannot be handled due to lack of data offsets in archive\n",
+                                                te->dumpId);
+               else if (!ctx->hasSeek)
+                       die_horribly(AH, modulename, "could not find block ID %d in archive -- "
+                                                "possibly due to out-of-order restore request, "
+                                 "which cannot be handled due to non-seekable input file\n",
+                                                te->dumpId);
+               else    /* huh, the dataPos led us to EOF? */
+                       die_horribly(AH, modulename, "could not find block ID %d in archive -- "
+                                                "possibly corrupt archive\n",
+                                                te->dumpId);
+       }
+
+       /* Are we sane? */
+       if (id != te->dumpId)
+               die_horribly(AH, modulename, "found unexpected block ID (%d) when reading data -- expected %d\n",
+                                        id, te->dumpId);
+
+       switch (blkType)
+       {
+               case BLK_DATA:
+                       _PrintData(AH);
+                       break;
+
+               case BLK_BLOBS:
+                       _LoadBlobs(AH, ropt->dropSchema);
+                       break;
+
+               default:                                /* Always have a default */
+                       die_horribly(AH, modulename, "unrecognized data block type %d while restoring archive\n",
+                                                blkType);
+                       break;
+       }
+}
+
+/*
+ * Print data from current file position.
+*/
+static void
+_PrintData(ArchiveHandle *AH)
+{
+       ReadDataFromArchive(AH, AH->compression, _CustomReadFunc);
+}
+
+static void
+_LoadBlobs(ArchiveHandle *AH, bool drop)
+{
+       Oid                     oid;
+
+       StartRestoreBlobs(AH);
+
+       oid = ReadInt(AH);
+       while (oid != 0)
+       {
+               StartRestoreBlob(AH, oid, drop);
+               _PrintData(AH);
+               EndRestoreBlob(AH, oid);
+               oid = ReadInt(AH);
+       }
+
+       EndRestoreBlobs(AH);
+}
+
+/*
+ * Skip the BLOBs from the current file position.
+ * BLOBS are written sequentially as data blocks (see below).
+ * Each BLOB is preceded by it's original OID.
+ * A zero OID indicated the end of the BLOBS
+ */
+static void
+_skipBlobs(ArchiveHandle *AH)
+{
+       Oid                     oid;
+
+       oid = ReadInt(AH);
+       while (oid != 0)
+       {
+               _skipData(AH);
+               oid = ReadInt(AH);
+       }
+}
+
+/*
+ * Skip data from current file position.
+ * Data blocks are formatted as an integer length, followed by data.
+ * A zero length denoted the end of the block.
+*/
+static void
+_skipData(ArchiveHandle *AH)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+       size_t          blkLen;
+       char       *buf = NULL;
+       int                     buflen = 0;
+       size_t          cnt;
+
+       blkLen = ReadInt(AH);
+       while (blkLen != 0)
+       {
+               if (blkLen > buflen)
+               {
+                       if (buf)
+                               free(buf);
+                       buf = (char *) pg_malloc(blkLen);
+                       buflen = blkLen;
+               }
+               cnt = fread(buf, 1, blkLen, AH->FH);
+               if (cnt != blkLen)
+               {
+                       if (feof(AH->FH))
+                               die_horribly(AH, modulename,
+                                                        "could not read from input file: end of file\n");
+                       else
+                               die_horribly(AH, modulename,
+                                       "could not read from input file: %s\n", strerror(errno));
+               }
+
+               ctx->filePos += blkLen;
+
+               blkLen = ReadInt(AH);
+       }
+
+       if (buf)
+               free(buf);
+}
+
+/*
+ * Write a byte of data to the archive.
+ *
+ * Mandatory.
+ *
+ * Called by the archiver to do integer & byte output to the archive.
+ */
+static int
+_WriteByte(ArchiveHandle *AH, const int i)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+       int                     res;
+
+       res = fputc(i, AH->FH);
+       if (res != EOF)
+               ctx->filePos += 1;
+       else
+               die_horribly(AH, modulename, "could not write byte: %s\n", strerror(errno));
+       return res;
+}
+
+/*
+ * Read a byte of data from the archive.
+ *
+ * Mandatory
+ *
+ * Called by the archiver to read bytes & integers from the archive.
+ * EOF should be treated as a fatal error.
+ */
+static int
+_ReadByte(ArchiveHandle *AH)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+       int                     res;
+
+       res = getc(AH->FH);
+       if (res == EOF)
+               die_horribly(AH, modulename, "unexpected end of file\n");
+       ctx->filePos += 1;
+       return res;
+}
+
+/*
+ * Write a buffer of data to the archive.
+ *
+ * Mandatory.
+ *
+ * Called by the archiver to write a block of bytes to the archive.
+ */
+static size_t
+_WriteBuf(ArchiveHandle *AH, const void *buf, size_t len)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+       size_t          res;
+
+       res = fwrite(buf, 1, len, AH->FH);
+
+       if (res != len)
+               die_horribly(AH, modulename,
+                                        "could not write to output file: %s\n", strerror(errno));
+
+       ctx->filePos += res;
+       return res;
+}
+
+/*
+ * Read a block of bytes from the archive.
+ *
+ * Mandatory.
+ *
+ * Called by the archiver to read a block of bytes from the archive
+ */
+static size_t
+_ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+       size_t          res;
+
+       res = fread(buf, 1, len, AH->FH);
+       ctx->filePos += res;
+
+       return res;
+}
+
+/*
+ * Close the archive.
+ *
+ * Mandatory.
+ *
+ * When writing the archive, this is the routine that actually starts
+ * the process of saving it to files. No data should be written prior
+ * to this point, since the user could sort the TOC after creating it.
+ *
+ * If an archive is to be written, this toutine must call:
+ *             WriteHead                       to save the archive header
+ *             WriteToc                        to save the TOC entries
+ *             WriteDataChunks         to save all DATA & BLOBs.
+ *
+ */
+static void
+_CloseArchive(ArchiveHandle *AH)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+       pgoff_t         tpos;
+
+       if (AH->mode == archModeWrite)
+       {
+               WriteHead(AH);
+               tpos = ftello(AH->FH);
+               WriteToc(AH);
+               ctx->dataStart = _getFilePos(AH, ctx);
+               WriteDataChunks(AH);
+
+               /*
+                * If possible, re-write the TOC in order to update the data offset
+                * information.  This is not essential, as pg_restore can cope in most
+                * cases without it; but it can make pg_restore significantly faster
+                * in some situations (especially parallel restore).
+                */
+               if (ctx->hasSeek &&
+                       fseeko(AH->FH, tpos, SEEK_SET) == 0)
+                       WriteToc(AH);
+       }
+
+       if (fclose(AH->FH) != 0)
+               die_horribly(AH, modulename, "could not close archive file: %s\n", strerror(errno));
+
+       AH->FH = NULL;
+}
+
+/*
+ * Reopen the archive's file handle.
+ *
+ * We close the original file handle, except on Windows.  (The difference
+ * is because on Windows, this is used within a multithreading context,
+ * and we don't want a thread closing the parent file handle.)
+ */
+static void
+_ReopenArchive(ArchiveHandle *AH)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+       pgoff_t         tpos;
+
+       if (AH->mode == archModeWrite)
+               die_horribly(AH, modulename, "can only reopen input archives\n");
+
+       /*
+        * These two cases are user-facing errors since they represent unsupported
+        * (but not invalid) use-cases.  Word the error messages appropriately.
+        */
+       if (AH->fSpec == NULL || strcmp(AH->fSpec, "") == 0)
+               die_horribly(AH, modulename, "parallel restore from stdin is not supported\n");
+       if (!ctx->hasSeek)
+               die_horribly(AH, modulename, "parallel restore from non-seekable file is not supported\n");
+
+       errno = 0;
+       tpos = ftello(AH->FH);
+       if (errno)
+               die_horribly(AH, modulename, "could not determine seek position in archive file: %s\n",
+                                        strerror(errno));
+
+#ifndef WIN32
+       if (fclose(AH->FH) != 0)
+               die_horribly(AH, modulename, "could not close archive file: %s\n",
+                                        strerror(errno));
+#endif
+
+       AH->FH = fopen(AH->fSpec, PG_BINARY_R);
+       if (!AH->FH)
+               die_horribly(AH, modulename, "could not open input file \"%s\": %s\n",
+                                        AH->fSpec, strerror(errno));
+
+       if (fseeko(AH->FH, tpos, SEEK_SET) != 0)
+               die_horribly(AH, modulename, "could not set seek position in archive file: %s\n",
+                                        strerror(errno));
+}
+
+/*
+ * Clone format-specific fields during parallel restoration.
+ */
+static void
+_Clone(ArchiveHandle *AH)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+
+       AH->formatData = (lclContext *) pg_malloc(sizeof(lclContext));
+       memcpy(AH->formatData, ctx, sizeof(lclContext));
+       ctx = (lclContext *) AH->formatData;
+
+       /* sanity check, shouldn't happen */
+       if (ctx->cs != NULL)
+               die_horribly(AH, modulename, "compressor active\n");
+
+       /*
+        * Note: we do not make a local lo_buf because we expect at most one BLOBS
+        * entry per archive, so no parallelism is possible.  Likewise,
+        * TOC-entry-local state isn't an issue because any one TOC entry is
+        * touched by just one worker child.
+        */
+}
+
+static void
+_DeClone(ArchiveHandle *AH)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+
+       free(ctx);
+}
+
+/*--------------------------------------------------
+ * END OF FORMAT CALLBACKS
+ *--------------------------------------------------
+ */
+
+/*
+ * Get the current position in the archive file.
+ */
+static pgoff_t
+_getFilePos(ArchiveHandle *AH, lclContext *ctx)
+{
+       pgoff_t         pos;
+
+       if (ctx->hasSeek)
+       {
+               pos = ftello(AH->FH);
+               if (pos != ctx->filePos)
+               {
+                       write_msg(modulename, "WARNING: ftell mismatch with expected position -- ftell used\n");
+
+                       /*
+                        * Prior to 1.7 (pg7.3) we relied on the internally maintained
+                        * pointer. Now we rely on ftello() always, unless the file has
+                        * been found to not support it.
+                        */
+               }
+       }
+       else
+               pos = ctx->filePos;
+       return pos;
+}
+
+/*
+ * Read a data block header. The format changed in V1.3, so we
+ * centralize the code here for simplicity.  Returns *type = EOF
+ * if at EOF.
+ */
+static void
+_readBlockHeader(ArchiveHandle *AH, int *type, int *id)
+{
+       lclContext *ctx = (lclContext *) AH->formatData;
+       int                     byt;
+
+       /*
+        * Note: if we are at EOF with a pre-1.3 input file, we'll die_horribly
+        * inside ReadInt rather than returning EOF.  It doesn't seem worth
+        * jumping through hoops to deal with that case better, because no such
+        * files are likely to exist in the wild: only some 7.1 development
+        * versions of pg_dump ever generated such files.
+        */
+       if (AH->version < K_VERS_1_3)
+               *type = BLK_DATA;
+       else
+       {
+               byt = getc(AH->FH);
+               *type = byt;
+               if (byt == EOF)
+               {
+                       *id = 0;                        /* don't return an uninitialized value */
+                       return;
+               }
+               ctx->filePos += 1;
+       }
+
+       *id = ReadInt(AH);
+}
+
+/*
+ * Callback function for WriteDataToArchive. Writes one block of (compressed)
+ * data to the archive.
+ */
+static size_t
+_CustomWriteFunc(ArchiveHandle *AH, const char *buf, size_t len)
+{
+       /* never write 0-byte blocks (this should not happen) */
+       if (len == 0)
+               return 0;
+
+       WriteInt(AH, len);
+       return _WriteBuf(AH, buf, len);
+}
+
+/*
+ * Callback function for ReadDataFromArchive. To keep things simple, we
+ * always read one compressed block at a time.
+ */
+static size_t
+_CustomReadFunc(ArchiveHandle *AH, char **buf, size_t *buflen)
+{
+       size_t          blkLen;
+       size_t          cnt;
+
+       /* Read length */
+       blkLen = ReadInt(AH);
+       if (blkLen == 0)
+               return 0;
+
+       /* If the caller's buffer is not large enough, allocate a bigger one */
+       if (blkLen > *buflen)
+       {
+               free(*buf);
+               *buf = (char *) pg_malloc(blkLen);
+               *buflen = blkLen;
+       }
+
+       cnt = _ReadBuf(AH, *buf, blkLen);
+       if (cnt != blkLen)
+       {
+               if (feof(AH->FH))
+                       die_horribly(AH, modulename,
+                                                "could not read from input file: end of file\n");
+               else
+                       die_horribly(AH, modulename,
+                                       "could not read from input file: %s\n", strerror(errno));
+       }
+       return cnt;
+}