04-tarutils-v2.patch
text/x-patch
Filename: 04-tarutils-v2.patch
Type: text/x-patch
Part: 1
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v2
| File | + | − |
|---|---|---|
| src/backend/replication/basebackup.c | 6 | 124 |
| src/bin/initdb/initdb.c | 0 | 30 |
| src/bin/pg_basebackup/pg_basebackup.c | 4 | 155 |
| src/bin/pg_dump/pg_backup_tar.c | 4 | 101 |
| src/include/port.h | 19 | 0 |
| src/port/Makefile | 1 | 1 |
| src/port/strutil.c | 87 | 0 |
| src/port/tarutil.c | 135 | 0 |
diff -durpN postgresql.3/src/backend/replication/basebackup.c postgresql.4/src/backend/replication/basebackup.c
--- postgresql.3/src/backend/replication/basebackup.c 2012-10-06 19:25:21.002166521 +0200
+++ postgresql.4/src/backend/replication/basebackup.c 2012-11-21 18:15:21.764511960 +0100
@@ -721,48 +721,12 @@ sendDir(char *path, int basepathlen, boo
/*
- * Utility routine to print possibly larger than 32 bit integers in a
- * portable fashion. Filled with zeros.
- */
-static void
-print_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;
- }
-}
-
-/*
* Maximum file size for a tar member: The limit inherent in the
* format is 2^33-1 bytes (nearly 8 GB). But we don't want to exceed
* what we can represent in pgoff_t.
*/
#define MAX_TAR_MEMBER_FILELEN (((int64) 1 << Min(33, sizeof(pgoff_t)*8 - 1)) - 1)
-static int
-_tarChecksum(char *header)
-{
- int i,
- sum;
-
- /*
- * Per POSIX, the checksum is the simple sum of all bytes in the header,
- * treating the bytes as unsigned, and treating the checksum field (at
- * offset 148) as though it contained 8 spaces.
- */
- sum = 8 * ' '; /* presumed value for checksum field */
- for (i = 0; i < 512; i++)
- if (i < 148 || i >= 156)
- sum += 0xFF & header[i];
- return sum;
-}
-
/* Given the member, write the TAR header & send the file */
static void
sendFile(char *readfilename, char *tarfilename, struct stat * statbuf)
@@ -840,94 +804,12 @@ _tarWriteHeader(const char *filename, co
{
char h[512];
- /*
- * Note: most of the fields in a tar header are not supposed to be
- * null-terminated. We use sprintf, which will write a null after the
- * required bytes; that null goes into the first byte of the next field.
- * This is okay as long as we fill the fields in order.
- */
- memset(h, 0, sizeof(h));
-
- /* Name 100 */
- sprintf(&h[0], "%.99s", filename);
- if (linktarget != NULL || S_ISDIR(statbuf->st_mode))
- {
- /*
- * We only support symbolic links to directories, and this is
- * indicated in the tar format by adding a slash at the end of the
- * name, the same as for regular directories.
- */
- int flen = strlen(filename);
-
- flen = Min(flen, 99);
- h[flen] = '/';
- h[flen + 1] = '\0';
- }
-
- /* Mode 8 */
- sprintf(&h[100], "%07o ", (int) statbuf->st_mode);
-
- /* User ID 8 */
- sprintf(&h[108], "%07o ", statbuf->st_uid);
-
- /* Group 8 */
- sprintf(&h[116], "%07o ", statbuf->st_gid);
-
- /* File size 12 - 11 digits, 1 space; use print_val for 64 bit support */
- if (linktarget != NULL || S_ISDIR(statbuf->st_mode))
- /* Symbolic link or directory has size zero */
- print_val(&h[124], 0, 8, 11);
- else
- print_val(&h[124], statbuf->st_size, 8, 11);
- sprintf(&h[135], " ");
-
- /* Mod Time 12 */
- sprintf(&h[136], "%011o ", (int) statbuf->st_mtime);
-
- /* Checksum 8 cannot be calculated until we've filled all other fields */
-
- if (linktarget != NULL)
- {
- /* Type - Symbolic link */
- sprintf(&h[156], "2");
- /* Link Name 100 */
- sprintf(&h[157], "%.99s", linktarget);
- }
- else if (S_ISDIR(statbuf->st_mode))
- /* Type - directory */
- sprintf(&h[156], "5");
- else
- /* Type - regular file */
- sprintf(&h[156], "0");
-
- /* Magic 6 */
- sprintf(&h[257], "ustar");
-
- /* Version 2 */
- sprintf(&h[263], "00");
-
- /* User 32 */
- /* XXX: Do we need to care about setting correct username? */
- sprintf(&h[265], "%.31s", "postgres");
-
- /* Group 32 */
- /* XXX: Do we need to care about setting correct group name? */
- sprintf(&h[297], "%.31s", "postgres");
-
- /* Major Dev 8 */
- sprintf(&h[329], "%07o ", 0);
-
- /* Minor Dev 8 */
- sprintf(&h[337], "%07o ", 0);
-
- /* Prefix 155 - not used, leave as nulls */
-
- /*
- * We mustn't overwrite the next field while inserting the checksum.
- * Fortunately, the checksum can't exceed 6 octal digits, so we just write
- * 6 digits, a space, and a null, which is legal per POSIX.
- */
- sprintf(&h[148], "%06o ", _tarChecksum(h));
+ _tarCreateHeader(h, filename, linktarget,
+ statbuf->st_size,
+ statbuf->st_mode,
+ statbuf->st_uid,
+ statbuf->st_gid,
+ statbuf->st_mtime);
/* Now send the completed header. */
pq_putmessage('d', h, 512);
diff -durpN postgresql.3/src/bin/initdb/initdb.c postgresql.4/src/bin/initdb/initdb.c
--- postgresql.3/src/bin/initdb/initdb.c 2012-11-21 18:03:54.547749709 +0100
+++ postgresql.4/src/bin/initdb/initdb.c 2012-11-21 18:27:34.223704650 +0100
@@ -218,7 +218,6 @@ static void make_postgres(void);
static void perform_fsync(void);
static void trapsig(int signum);
static void check_ok(void);
-static char *escape_quotes(const char *src);
static int locale_date_order(const char *locale);
static bool check_locale_name(int category, const char *locale,
char **canonname);
@@ -2342,35 +2341,6 @@ check_ok(void)
}
}
-/*
- * Escape (by doubling) any single quotes or backslashes in given string
- *
- * Note: this is used to process both postgresql.conf entries and SQL
- * string literals. Since postgresql.conf strings are defined to treat
- * backslashes as escapes, we have to double backslashes here. Hence,
- * when using this for a SQL string literal, use E'' syntax.
- *
- * We do not need to worry about encoding considerations because all
- * valid backend encodings are ASCII-safe.
- */
-static char *
-escape_quotes(const char *src)
-{
- int len = strlen(src),
- i,
- j;
- char *result = pg_malloc(len * 2 + 1);
-
- for (i = 0, j = 0; i < len; i++)
- {
- if (SQL_STR_DOUBLE(src[i], true))
- result[j++] = src[i];
- result[j++] = src[i];
- }
- result[j] = '\0';
- return result;
-}
-
/* Hack to suppress a warning about %x from some versions of gcc */
static inline size_t
my_strftime(char *s, size_t max, const char *fmt, const struct tm * tm)
diff -durpN postgresql.3/src/bin/pg_basebackup/pg_basebackup.c postgresql.4/src/bin/pg_basebackup/pg_basebackup.c
--- postgresql.3/src/bin/pg_basebackup/pg_basebackup.c 2012-11-21 18:05:17.308315854 +0100
+++ postgresql.4/src/bin/pg_basebackup/pg_basebackup.c 2012-11-21 18:28:02.647904139 +0100
@@ -26,7 +26,6 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
-#include <time.h>
#ifdef HAVE_LIBZ
#include <zlib.h>
@@ -73,8 +72,6 @@ static int has_xlogendptr = 0;
static volatile LONG has_xlogendptr = 0;
#endif
-/* Don't ever change this value, the TAR file format requires it. */
-#define TARCHUNKSZ 512
PQExpBuffer rcExpBuf = NULL;
/* Function headers */
@@ -84,13 +81,8 @@ static void progress_report(int tablespa
static void ReceiveTarFile(PGconn *conn, PGresult *res, int rownum);
static void ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum);
-static char *escape_quotes(const char *src);
static void CreateRecoveryConf(PGconn *conn);
static void WriteRecoveryConf(void);
-static int _tarChecksum(char *header);
-static void print_val(char *s, uint64 val, unsigned int base, size_t len);
-static void scan_val(char *s, uint64 *val, unsigned int base, size_t len);
-static void _tarCreateHeader(char *header, char *filename, size_t filesize);
static void BaseBackup(void);
static bool reached_end_position(XLogRecPtr segendpos, uint32 timeline,
@@ -683,7 +675,10 @@ ReceiveTarFile(PGconn *conn, PGresult *r
char header[TARCHUNKSZ];
int padding;
- _tarCreateHeader(header, "recovery.conf", rcExpBuf->len);
+ _tarCreateHeader(header, "recovery.conf", NULL,
+ rcExpBuf->len,
+ 0600, 04000, 02000,
+ time(NULL));
padding = ((rcExpBuf->len + (TARCHUNKSZ-1)) & ~(TARCHUNKSZ-1)) - rcExpBuf->len;
@@ -1110,152 +1105,6 @@ ReceiveAndUnpackTarFile(PGconn *conn, PG
WriteRecoveryConf();
}
-static int
-_tarChecksum(char *header)
-{
- int i,
- sum;
-
- /*
- * Per POSIX, the checksum is the simple sum of all bytes in the header,
- * treating the bytes as unsigned, and treating the checksum field (at
- * offset 148) as though it contained 8 spaces.
- */
- sum = 8 * ' '; /* presumed value for checksum field */
- for (i = 0; i < TARCHUNKSZ; i++)
- if (i < 148 || i >= 156)
- sum += 0xFF & header[i];
- return sum;
-}
-
-
-/*
- * Utility routine to print possibly larger than 32 bit integers in a
- * portable fashion. Filled with zeros.
- */
-static void
-print_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;
- }
-}
-
-
-/*
- * Inverse for print_val()
- */
-static void
-scan_val(char *s, uint64 *val, unsigned int base, size_t len)
-{
- uint64 tmp = 0;
- int i;
-
- for (i = 0; i < len; i++)
- {
- int digit = s[i] - '0';
-
- tmp = tmp * base + digit;
- }
-
- *val = tmp;
-}
-
-
-static void
-_tarCreateHeader(char *header, char *filename, size_t filesize)
-{
- /*
- * Note: most of the fields in a tar header are not supposed to be
- * null-terminated. We use sprintf, which will write a null after the
- * required bytes; that null goes into the first byte of the next field.
- * This is okay as long as we fill the fields in order.
- */
- memset(header, 0, TARCHUNKSZ /* sizeof the tar header */);
-
- /* Name 100 */
- sprintf(&header[0], "%.99s", filename);
-
- /* Mode 8 */
- sprintf(&header[100], "0000600 ");
-
- /* User ID 8 */
- sprintf(&header[108], "0004000 ");
-
- /* Group 8 */
- sprintf(&header[116], "0002000 ");
-
- /* File size 12 - 11 digits, 1 space; use print_val for 64 bit support */
- print_val(&header[124], filesize, 8, 11);
- sprintf(&header[135], " ");
-
- /* Mod Time 12 */
- sprintf(&header[136], "%011o ", (int) time(NULL));
-
- /* Checksum 8 cannot be calculated until we've filled all other fields */
-
- /* Type - regular file */
- sprintf(&header[156], "0");
-
- /* Link Name 100 (leave as nulls) */
-
- /* Magic 6 */
- sprintf(&header[257], "ustar");
-
- /* Version 2 */
- sprintf(&header[263], "00");
-
- /* User 32 */
- /* XXX: Do we need to care about setting correct username? */
- sprintf(&header[265], "%.31s", "postgres");
-
- /* Group 32 */
- /* XXX: Do we need to care about setting correct group name? */
- sprintf(&header[297], "%.31s", "postgres");
-
- /* Major Dev 8 */
- sprintf(&header[329], "%07o ", 0);
-
- /* Minor Dev 8 */
- sprintf(&header[337], "%07o ", 0);
-
- /* Prefix 155 - not used, leave as nulls */
-
- /*
- * We mustn't overwrite the next field while inserting the checksum.
- * Fortunately, the checksum can't exceed 6 octal digits, so we just write
- * 6 digits, a space, and a null, which is legal per POSIX.
- */
- sprintf(&header[148], "%06o ", _tarChecksum(header));
-}
-
-/*
- * Escape single quotes in a string
- */
-static char *
-escape_quotes(const char *src)
-{
- int len = strlen(src),
- i,
- j;
- char *result = pg_malloc(len * 2 + 1);
-
- for (i = 0, j = 0; i < len; i++)
- {
- if (SQL_STR_DOUBLE(src[i], true))
- result[j++] = src[i];
- result[j++] = src[i];
- }
- result[j] = '\0';
- return result;
-}
-
/*
* Try to create recovery.conf in memory and set the length to write later.
*/
diff -durpN postgresql.3/src/bin/pg_dump/pg_backup_tar.c postgresql.4/src/bin/pg_dump/pg_backup_tar.c
--- postgresql.3/src/bin/pg_dump/pg_backup_tar.c 2012-11-21 16:59:40.614991367 +0100
+++ postgresql.4/src/bin/pg_dump/pg_backup_tar.c 2012-11-21 18:15:21.767511985 +0100
@@ -114,7 +114,6 @@ static char *tarGets(char *buf, size_t l
static int tarPrintf(ArchiveHandle *AH, TAR_MEMBER *th, const char *fmt,...) __attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));
static void _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th);
-static int _tarChecksum(char *th);
static TAR_MEMBER *_tarPositionTo(ArchiveHandle *AH, const char *filename);
static size_t tarRead(void *buf, size_t len, TAR_MEMBER *th);
static size_t tarWrite(const void *buf, size_t len, TAR_MEMBER *th);
@@ -1015,24 +1014,6 @@ tarPrintf(ArchiveHandle *AH, TAR_MEMBER
return cnt;
}
-static int
-_tarChecksum(char *header)
-{
- int i,
- sum;
-
- /*
- * Per POSIX, the checksum is the simple sum of all bytes in the header,
- * treating the bytes as unsigned, and treating the checksum field (at
- * offset 148) as though it contained 8 spaces.
- */
- sum = 8 * ' '; /* presumed value for checksum field */
- for (i = 0; i < 512; i++)
- if (i < 148 || i >= 156)
- sum += 0xFF & header[i];
- return sum;
-}
-
bool
isValidTarHeader(char *header)
{
@@ -1304,94 +1285,16 @@ _tarGetHeader(ArchiveHandle *AH, TAR_MEM
}
-/*
- * Utility routine to print possibly larger than 32 bit integers in a
- * portable fashion. Filled with zeros.
- */
-static void
-print_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];
- /*
- * Note: most of the fields in a tar header are not supposed to be
- * null-terminated. We use sprintf, which will write a null after the
- * required bytes; that null goes into the first byte of the next field.
- * This is okay as long as we fill the fields in order.
- */
- memset(h, 0, sizeof(h));
-
- /* Name 100 */
- sprintf(&h[0], "%.99s", th->targetFile);
-
- /* Mode 8 */
- sprintf(&h[100], "0000600 ");
-
- /* User ID 8 */
- sprintf(&h[108], "0004000 ");
-
- /* Group 8 */
- sprintf(&h[116], "0002000 ");
-
- /* File size 12 - 11 digits, 1 space; use print_val for 64 bit support */
- print_val(&h[124], th->fileLen, 8, 11);
- sprintf(&h[135], " ");
-
- /* Mod Time 12 */
- sprintf(&h[136], "%011o ", (int) time(NULL));
-
- /* Checksum 8 cannot be calculated until we've filled all other fields */
-
- /* Type - regular file */
- sprintf(&h[156], "0");
-
- /* Link Name 100 (leave as nulls) */
-
- /* Magic 6 */
- sprintf(&h[257], "ustar");
-
- /* Version 2 */
- sprintf(&h[263], "00");
-
- /* User 32 */
- /* XXX: Do we need to care about setting correct username? */
- sprintf(&h[265], "%.31s", "postgres");
-
- /* Group 32 */
- /* XXX: Do we need to care about setting correct group name? */
- sprintf(&h[297], "%.31s", "postgres");
-
- /* Major Dev 8 */
- sprintf(&h[329], "%07o ", 0);
-
- /* Minor Dev 8 */
- sprintf(&h[337], "%07o ", 0);
-
- /* Prefix 155 - not used, leave as nulls */
-
- /*
- * We mustn't overwrite the next field while inserting the checksum.
- * Fortunately, the checksum can't exceed 6 octal digits, so we just write
- * 6 digits, a space, and a null, which is legal per POSIX.
- */
- sprintf(&h[148], "%06o ", _tarChecksum(h));
+ _tarCreateHeader(h, th->targetFile, NULL,
+ th->fileLen,
+ 0600, 04000, 02000,
+ time(NULL));
- /* Now write the completed header. */
if (fwrite(h, 1, 512, th->tarFH) != 512)
exit_horribly(modulename, "could not write to output file: %s\n", strerror(errno));
}
diff -durpN postgresql.3/src/include/port.h postgresql.4/src/include/port.h
--- postgresql.3/src/include/port.h 2012-11-21 18:02:18.860095054 +0100
+++ postgresql.4/src/include/port.h 2012-11-21 18:27:22.006619619 +0100
@@ -16,6 +16,7 @@
#include <ctype.h>
#include <netdb.h>
#include <pwd.h>
+#include <time.h>
/* port/init.c */
typedef void (*fe_func)(void);
@@ -491,4 +492,22 @@ extern void pg_free(void *ptr);
/* port/pgmkdirp.c */
extern int pg_mkdir_p(char *path, int omode);
+/* port/strutil.c */
+extern void print_val(char *s, uint64 val, unsigned int base, size_t len);
+extern void scan_val(char *s, uint64 *val, unsigned int base, size_t len);
+extern char *escape_quotes(const char *src);
+
+/* port/tarutil.c */
+
+/* Don't ever change this value, the TAR file format requires it. */
+#define TARCHUNKSZ 512
+
+extern int _tarChecksum(char *header);
+extern void _tarCreateHeader(char *header,
+ const char *filename,
+ const char *linktarget,
+ int64 filesize, int st_mode,
+ int st_uid, int st_gid,
+ time_t mtime);
+
#endif /* PG_PORT_H */
diff -durpN postgresql.3/src/port/Makefile postgresql.4/src/port/Makefile
--- postgresql.3/src/port/Makefile 2012-11-21 16:18:29.251969612 +0100
+++ postgresql.4/src/port/Makefile 2012-11-21 18:18:11.002685027 +0100
@@ -33,7 +33,7 @@ LIBS += $(PTHREAD_LIBS)
OBJS = $(LIBOBJS) chklocale.o dirmod.o erand48.o exec.o fls.o init.o \
inet_net_ntop.o noblock.o path.o pgcheckdir.o pg_crc.o pgmalloc.o \
pgmkdirp.o pgsleep.o pgstrcasecmp.o qsort.o qsort_arg.o sprompt.o \
- thread.o
+ strutil.o tarutil.o thread.o
# foo_srv.o and foo.o are both built from foo.c, but only foo.o has -DFRONTEND
OBJS_SRV = $(OBJS:%.o=%_srv.o)
diff -durpN postgresql.3/src/port/strutil.c postgresql.4/src/port/strutil.c
--- postgresql.3/src/port/strutil.c 1970-01-01 01:00:00.000000000 +0100
+++ postgresql.4/src/port/strutil.c 2012-11-21 18:33:28.833248501 +0100
@@ -0,0 +1,87 @@
+/*-------------------------------------------------------------------------
+ *
+ * strutil.c
+ * common string functions
+ *
+ * IDENTIFICATION
+ * src/port/strutil.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FRONTEND
+#include "postgres.h"
+#include "utils/palloc.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+/*
+ * Utility routine to print possibly larger than 32 bit integers in a
+ * portable fashion. Filled with zeros.
+ */
+void
+print_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;
+ }
+}
+
+
+/*
+ * Inverse of print_val()
+ */
+void
+scan_val(char *s, uint64 *val, unsigned int base, size_t len)
+{
+ uint64 tmp = 0;
+ int i;
+
+ for (i = 0; i < len; i++)
+ {
+ int digit = s[i] - '0';
+
+ tmp = tmp * base + digit;
+ }
+
+ *val = tmp;
+}
+
+/*
+ * Escape (by doubling) any single quotes or backslashes in given string
+ *
+ * Note: this is used to process both postgresql.conf entries and SQL
+ * string literals. Since postgresql.conf strings are defined to treat
+ * backslashes as escapes, we have to double backslashes here. Hence,
+ * when using this for a SQL string literal, use E'' syntax.
+ *
+ * We do not need to worry about encoding considerations because all
+ * valid backend encodings are ASCII-safe.
+ */
+char *
+escape_quotes(const char *src)
+{
+ int len = strlen(src),
+ i,
+ j;
+#ifdef FRONTEND
+ char *result = pg_malloc(len * 2 + 1);
+#else
+ char *result = palloc(len * 2 + 1);
+#endif
+
+ for (i = 0, j = 0; i < len; i++)
+ {
+ if (SQL_STR_DOUBLE(src[i], true))
+ result[j++] = src[i];
+ result[j++] = src[i];
+ }
+ result[j] = '\0';
+ return result;
+}
diff -durpN postgresql.3/src/port/tarutil.c postgresql.4/src/port/tarutil.c
--- postgresql.3/src/port/tarutil.c 1970-01-01 01:00:00.000000000 +0100
+++ postgresql.4/src/port/tarutil.c 2012-11-21 18:16:25.537953568 +0100
@@ -0,0 +1,135 @@
+/*-------------------------------------------------------------------------
+ *
+ * tarutil.c
+ * common functions for handling the TAR format
+ *
+ * IDENTIFICATION
+ * src/port/tarutil.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <sys/stat.h>
+
+int
+_tarChecksum(char *header)
+{
+ int i,
+ sum;
+
+ /*
+ * Per POSIX, the checksum is the simple sum of all bytes in the header,
+ * treating the bytes as unsigned, and treating the checksum field (at
+ * offset 148) as though it contained 8 spaces.
+ */
+ sum = 8 * ' '; /* presumed value for checksum field */
+ for (i = 0; i < TARCHUNKSZ; i++)
+ if (i < 148 || i >= 156)
+ sum += 0xFF & header[i];
+ return sum;
+}
+
+
+void
+_tarCreateHeader(char *header, const char *filename,
+ const char *linktarget,
+ int64 filesize, int st_mode,
+ int st_uid, int st_gid,
+ time_t mtime)
+{
+ /*
+ * Note: most of the fields in a tar header are not supposed to be
+ * null-terminated. We use sprintf, which will write a null after the
+ * required bytes; that null goes into the first byte of the next field.
+ * This is okay as long as we fill the fields in order.
+ */
+ memset(header, 0, TARCHUNKSZ /* sizeof the tar header */);
+
+ /* Name 100 */
+ sprintf(&header[0], "%.99s", filename);
+ if (linktarget != NULL || S_ISDIR(st_mode))
+ {
+ /*
+ * We only support symbolic links to directories, and this is
+ * indicated in the tar format by adding a slash at the end of the
+ * name, the same as for regular directories.
+ */
+ int flen = strlen(filename);
+
+ flen = Min(flen, 99);
+ header[flen] = '/';
+ header[flen + 1] = '\0';
+ }
+
+ /* Mode 8 */
+ sprintf(&header[100], "%07o ", st_mode);
+
+ /* User ID 8 */
+ sprintf(&header[108], "%07o ", st_uid);
+
+ /* Group 8 */
+ sprintf(&header[116], "%07o ", st_gid);
+
+ /* File size 12 - 11 digits, 1 space; use print_val for 64 bit support */
+ if (linktarget != NULL || S_ISDIR(st_mode))
+ /* Symbolic link or directory has size zero */
+ print_val(&header[124], 0, 8, 11);
+ else
+ print_val(&header[124], filesize, 8, 11);
+ sprintf(&header[135], " ");
+
+ /* Mod Time 12 - 11 digits, 1 space; use print_val for 64 bit support */
+ print_val(&header[136], mtime, 8, 11);
+ sprintf(&header[147], " ");
+
+ /* Checksum 8 cannot be calculated until we've filled all other fields */
+
+ if (linktarget != NULL)
+ {
+ /* Type - Symbolic link */
+ sprintf(&header[156], "2");
+ /* Link Name 100 */
+ sprintf(&header[157], "%.99s", linktarget);
+ }
+ else if (S_ISDIR(st_mode))
+ /* Type - directory */
+ sprintf(&header[156], "5");
+ else
+ /* Type - regular file */
+ sprintf(&header[156], "0");
+
+ /* Magic 6 */
+ sprintf(&header[257], "ustar");
+
+ /* Version 2 */
+ sprintf(&header[263], "00");
+
+ /* User 32 */
+ /* XXX: Do we need to care about setting correct username? */
+ sprintf(&header[265], "%.31s", "postgres");
+
+ /* Group 32 */
+ /* XXX: Do we need to care about setting correct group name? */
+ sprintf(&header[297], "%.31s", "postgres");
+
+ /* Major Dev 8 */
+ sprintf(&header[329], "%07o ", 0);
+
+ /* Minor Dev 8 */
+ sprintf(&header[337], "%07o ", 0);
+
+ /* Prefix 155 - not used, leave as nulls */
+
+ /*
+ * We mustn't overwrite the next field while inserting the checksum.
+ * Fortunately, the checksum can't exceed 6 octal digits, so we just write
+ * 6 digits, a space, and a null, which is legal per POSIX.
+ */
+ sprintf(&header[148], "%06o ", _tarChecksum(header));
+}
+