📄 pg_backup_tar.c
字号:
}static void_LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt){ Oid oid; lclContext *ctx = (lclContext *) AH->formatData; TAR_MEMBER *th; size_t cnt; char buf[4096]; StartRestoreBlobs(AH); th = tarOpen(AH, NULL, 'r'); /* Open next file */ while (th != NULL) { ctx->FH = th; if (strncmp(th->targetFile, "blob_", 5) == 0) { oid = atooid(&th->targetFile[5]); if (oid != 0) { ahlog(AH, 1, "restoring large object OID %u\n", oid); StartRestoreBlob(AH, oid); while ((cnt = tarRead(buf, 4095, th)) > 0) { buf[cnt] = '\0'; ahwrite(buf, 1, cnt, AH); } EndRestoreBlob(AH, oid); } } tarClose(AH, th); th = tarOpen(AH, NULL, 'r'); } EndRestoreBlobs(AH);}static int_WriteByte(ArchiveHandle *AH, const int i){ lclContext *ctx = (lclContext *) AH->formatData; int res; char b = i; /* Avoid endian problems */ res = tarWrite(&b, 1, ctx->FH); if (res != EOF) ctx->filePos += res; return res;}static int_ReadByte(ArchiveHandle *AH){ lclContext *ctx = (lclContext *) AH->formatData; int res; char c = '\0'; res = tarRead(&c, 1, ctx->FH); if (res != EOF) ctx->filePos += res; return c;}static size_t_WriteBuf(ArchiveHandle *AH, const void *buf, size_t len){ lclContext *ctx = (lclContext *) AH->formatData; size_t res; res = tarWrite((void *) buf, len, ctx->FH); ctx->filePos += res; return res;}static size_t_ReadBuf(ArchiveHandle *AH, void *buf, size_t len){ lclContext *ctx = (lclContext *) AH->formatData; size_t res; res = tarRead(buf, len, ctx->FH); ctx->filePos += res; return res;}static void_CloseArchive(ArchiveHandle *AH){ lclContext *ctx = (lclContext *) AH->formatData; TAR_MEMBER *th; RestoreOptions *ropt; int savVerbose, i; if (AH->mode == archModeWrite) { /* * Write the Header & TOC to the archive FIRST */ th = tarOpen(AH, "toc.dat", 'w'); ctx->FH = th; WriteHead(AH); WriteToc(AH); tarClose(AH, th); /* Not needed any more */ /* * Now send the data (tables & blobs) */ WriteDataChunks(AH); /* * Now this format wants to append a script which does a full restore * if the files have been extracted. */ th = tarOpen(AH, "restore.sql", 'w'); tarPrintf(AH, th, "create temporary table pgdump_restore_path(p text);\n"); tarPrintf(AH, th, "--\n" "-- NOTE:\n" "--\n" "-- File paths need to be edited. Search for $$PATH$$ and\n" "-- replace it with the path to the directory containing\n" "-- the extracted data files.\n" "--\n" "-- Edit the following to match the path where the\n" "-- tar archive has been extracted.\n" "--\n"); tarPrintf(AH, th, "insert into pgdump_restore_path values('/tmp');\n\n"); AH->CustomOutPtr = _scriptOut; ctx->isSpecialScript = 1; ctx->scriptTH = th; ropt = NewRestoreOptions(); ropt->dropSchema = 1; ropt->compression = 0; ropt->superuser = NULL; ropt->suppressDumpWarnings = true; savVerbose = AH->public.verbose; AH->public.verbose = 0; RestoreArchive((Archive *) AH, ropt); AH->public.verbose = savVerbose; tarClose(AH, th); /* Add a block of NULLs since it's de-rigeur. */ for (i = 0; i < 512; i++) { if (fputc(0, ctx->tarFH) == EOF) die_horribly(AH, modulename, "could not write null block at end of tar archive\n"); } } AH->FH = NULL;}static size_t_scriptOut(ArchiveHandle *AH, const void *buf, size_t len){ lclContext *ctx = (lclContext *) AH->formatData; return tarWrite(buf, len, ctx->scriptTH);}/* * BLOB support *//* * 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; char fname[K_STD_BUF_SIZE]; sprintf(fname, "blobs.toc"); ctx->blobToc = tarOpen(AH, fname, 'w');}/* * 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; lclTocEntry *tctx = (lclTocEntry *) te->formatData; char fname[255]; char *sfx; if (oid == 0) die_horribly(AH, modulename, "invalid OID for large object (%u)\n", oid); if (AH->compression != 0) sfx = ".gz"; else sfx = ""; sprintf(fname, "blob_%u.dat%s", oid, sfx); tarPrintf(AH, ctx->blobToc, "%u %s\n", oid, fname); tctx->TH = tarOpen(AH, fname, 'w');}/* * Called by the archiver when the dumper calls EndBlob. * * Optional. * */static void_EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid){ lclTocEntry *tctx = (lclTocEntry *) te->formatData; tarClose(AH, tctx->TH);}/* * Called by the archiver when finishing saving all BLOB DATA. * * Optional. * */static void_EndBlobs(ArchiveHandle *AH, TocEntry *te){ lclContext *ctx = (lclContext *) AH->formatData; /* Write out a fake zero OID to mark end-of-blobs. */ /* WriteInt(AH, 0); */ tarClose(AH, ctx->blobToc);}/*------------ * TAR Support *------------ */static inttarPrintf(ArchiveHandle *AH, TAR_MEMBER *th, const char *fmt,...){ char *p = NULL; va_list ap; size_t bSize = strlen(fmt) + 256; /* Should be enough */ int cnt = -1; /* * This is paranoid: deal with the possibility that vsnprintf is willing * to ignore trailing null */ /* * or returns > 0 even if string does not fit. It may be the case that it * returns cnt = bufsize */ while (cnt < 0 || cnt >= (bSize - 1)) { if (p != NULL) free(p); bSize *= 2; p = (char *) malloc(bSize); if (p == NULL) die_horribly(AH, modulename, "out of memory\n"); va_start(ap, fmt); cnt = vsnprintf(p, bSize, fmt, ap); va_end(ap); } cnt = tarWrite(p, cnt, th); free(p); return cnt;}static int_tarChecksum(char *header){ int i, sum; sum = 0; for (i = 0; i < 512; i++) if (i < 148 || i >= 156) sum += 0xFF & header[i]; return sum + 256; /* Assume 8 blanks in checksum field */}boolisValidTarHeader(char *header){ int sum; int chk = _tarChecksum(header); sscanf(&header[148], "%8o", &sum); if (sum != chk) return false; /* POSIX format */ if (strncmp(&header[257], "ustar00", 7) == 0) return true; /* older format */ if (strncmp(&header[257], "ustar ", 7) == 0) return true; return false;}/* Given the member, write the TAR header & copy the file */static void_tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th){ lclContext *ctx = (lclContext *) AH->formatData; FILE *tmp = th->tmpFH; /* Grab it for convenience */ char buf[32768]; size_t cnt; off_t len = 0; size_t res; size_t i, pad; /* * Find file len & go back to start. */ fseeko(tmp, 0, SEEK_END); th->fileLen = ftello(tmp); /* * Some compilers with throw a warning knowing this test can never be true * because off_t can't exceed the compared maximum. */ if (th->fileLen > MAX_TAR_MEMBER_FILELEN) die_horribly(AH, modulename, "archive member too large for tar format\n"); fseeko(tmp, 0, SEEK_SET); _tarWriteHeader(th); while ((cnt = fread(&buf[0], 1, 32767, tmp)) > 0) { res = fwrite(&buf[0], 1, cnt, th->tarFH); if (res != cnt) die_horribly(AH, modulename, "write error appending to tar archive (wrote %lu, attempted %lu)\n", (unsigned long) res, (unsigned long) cnt); len += res; } if (fclose(tmp) != 0) /* This *should* delete it... */ die_horribly(AH, modulename, "could not close tar member: %s\n", strerror(errno)); if (len != th->fileLen) { char buf1[100], buf2[100]; snprintf(buf1, sizeof(buf1), INT64_FORMAT, (int64) len); snprintf(buf2, sizeof(buf2), INT64_FORMAT, (int64) th->pos); die_horribly(AH, modulename, "actual file length (%s) does not match expected (%s)\n", buf1, buf2); } pad = ((len + 511) & ~511) - len; for (i = 0; i < pad; i++) { if (fputc('\0', th->tarFH) == EOF) die_horribly(AH, modulename, "could not output padding at end of tar member\n"); } ctx->tarFHpos += len + pad;}/* Locate the file in the archive, read header and position to data */static TAR_MEMBER *_tarPositionTo(ArchiveHandle *AH, const char *filename){ lclContext *ctx = (lclContext *) AH->formatData; TAR_MEMBER *th = calloc(1, sizeof(TAR_MEMBER)); char c; char header[512]; size_t i, len, blks; int id; th->AH = AH; /* Go to end of current file, if any */ if (ctx->tarFHpos != 0) { char buf1[100], buf2[100]; snprintf(buf1, sizeof(buf1), INT64_FORMAT, (int64) ctx->tarFHpos); snprintf(buf2, sizeof(buf2), INT64_FORMAT, (int64) ctx->tarNextMember); ahlog(AH, 4, "moving from position %s to next member at file position %s\n", buf1, buf2); while (ctx->tarFHpos < ctx->tarNextMember) _tarReadRaw(AH, &c, 1, NULL, ctx->tarFH); } { char buf[100]; snprintf(buf, sizeof(buf), INT64_FORMAT, (int64) ctx->tarFHpos); ahlog(AH, 4, "now at file position %s\n", buf); } /* We are at the start of the file. or at the next member */ /* Get the header */ if (!_tarGetHeader(AH, th)) { if (filename) die_horribly(AH, modulename, "could not find header for file %s in tar archive\n", filename); else /* * We're just scanning the archibe for the next file, so return * null */ { free(th); return NULL; } } while (filename != NULL && strcmp(th->targetFile, filename) != 0) { ahlog(AH, 4, "skipping tar member %s\n", th->targetFile); id = atoi(th->targetFile); if ((TocIDRequired(AH, id, AH->ropt) & REQ_DATA) != 0) die_horribly(AH, modulename, "dumping data out of order is not supported in this archive format: " "%s is required, but comes before %s in the archive file.\n", th->targetFile, filename); /* Header doesn't match, so read to next header */ len = ((th->fileLen + 511) & ~511); /* Padded length */ blks = len >> 9; /* # of 512 byte blocks */ for (i = 0; i < blks; i++) _tarReadRaw(AH, &header[0], 512, NULL, ctx->tarFH); if (!_tarGetHeader(AH, th)) die_horribly(AH, modulename, "could not find header for file %s in tar archive\n", filename); } ctx->tarNextMember = ctx->tarFHpos + ((th->fileLen + 511) & ~511); th->pos = 0; return th;}/* Read & verify a header */static int_tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th){ lclContext *ctx = (lclContext *) AH->formatData; char h[512]; char tag[100]; int sum, chk; size_t len; unsigned long ullen; off_t hPos; bool gotBlock = false; while (!gotBlock) {#if 0 if (ftello(ctx->tarFH) != ctx->tarFHpos) { char buf1[100], buf2[100]; snprintf(buf1, sizeof(buf1), INT64_FORMAT, (int64) ftello(ctx->tarFH)); snprintf(buf2, sizeof(buf2), INT64_FORMAT, (int64) ftello(ctx->tarFHpos)); die_horribly(AH, modulename, "mismatch in actual vs. predicted file position (%s vs. %s)\n", buf1, buf2); }#endif /* Save the pos for reporting purposes */ hPos = ctx->tarFHpos; /* Read a 512 byte block, return EOF, exit if short */ len = _tarReadRaw(AH, h, 512, NULL, ctx->tarFH); if (len == 0) /* EOF */ return 0; if (len != 512) die_horribly(AH, modulename, "incomplete tar header found (%lu bytes)\n", (unsigned long) len); /* Calc checksum */ chk = _tarChecksum(h); sscanf(&h[148], "%8o", &sum); /* * If the checksum failed, see if it is a null block. If so, silently * continue to the next block. */ if (chk == sum) gotBlock = true; else { int i; for (i = 0; i < 512; i++) { if (h[i] != 0) { gotBlock = true; break; } } } } sscanf(&h[0], "%99s", tag); sscanf(&h[124], "%12lo", &ullen); len = (size_t) ullen; { char buf[100]; snprintf(buf, sizeof(buf), INT64_FORMAT, (int64) hPos); ahlog(AH, 3, "TOC Entry %s at %s (length %lu, checksum %d)\n", tag, buf, (unsigned long) len, sum); } if (chk != sum) { char buf[100]; snprintf(buf, sizeof(buf), INT64_FORMAT, (int64) ftello(ctx->tarFH)); die_horribly(AH, modulename, "corrupt tar header found in %s " "(expected %d, computed %d) file position %s\n", tag, sum, chk, buf); } th->targetFile = strdup(tag); th->fileLen = len; return 1;}/* * Utility routine to print possibly larger than 32 bit integers in a * portable fashion. Filled with zeros. */static voidprint_val(char *s, uint64 val, unsigned int base, size_t len){ int i; for (i = len; i > 0; i--) { int digit = val % base; s[i - 1] = '0' + digit; val = val / base; }}static void_tarWriteHeader(TAR_MEMBER *th){ char h[512]; int lastSum = 0; int sum; memset(h, 0, sizeof(h)); /* Name 100 */ sprintf(&h[0], "%.99s", th->targetFile); /* Mode 8 */ sprintf(&h[100], "100600 "); /* User ID 8 */ sprintf(&h[108], "004000 "); /* Group 8 */ sprintf(&h[116], "002000 "); /* File size 12 - 11 digits, 1 space, no NUL */ print_val(&h[124], th->fileLen, 8, 11); sprintf(&h[135], " "); /* Mod Time 12 */ sprintf(&h[136], "%011o ", (int) time(NULL)); /* Checksum 8 */ sprintf(&h[148], "%06o ", lastSum); /* Type - regular file */ sprintf(&h[156], "0"); /* Link tag 100 (NULL) */ /* Magic 6 + Version 2 */ sprintf(&h[257], "ustar00");#if 0 /* User 32 */ sprintf(&h[265], "%.31s", ""); /* How do I get username reliably? Do * I need to? */ /* Group 32 */ sprintf(&h[297], "%.31s", ""); /* How do I get group reliably? Do I * need to? */ /* Maj Dev 8 */ sprintf(&h[329], "%6o ", 0); /* Min Dev 8 */ sprintf(&h[337], "%6o ", 0);#endif while ((sum = _tarChecksum(h)) != lastSum) { sprintf(&h[148], "%06o ", sum); lastSum = sum; } if (fwrite(h, 1, 512, th->tarFH) != 512) die_horribly(th->AH, modulename, "could not write tar header\n");}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -