aboutsummaryrefslogtreecommitdiff
path: root/src/port/tar.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/port/tar.c')
-rw-r--r--src/port/tar.c128
1 files changed, 90 insertions, 38 deletions
diff --git a/src/port/tar.c b/src/port/tar.c
index 72fd4e13aca..52a2113a47e 100644
--- a/src/port/tar.c
+++ b/src/port/tar.c
@@ -3,21 +3,80 @@
#include <sys/stat.h>
/*
- * Utility routine to print possibly larger than 32 bit integers in a
- * portable fashion. Filled with zeros.
+ * Print a numeric field in a tar header. The field starts at *s and is of
+ * length len; val is the value to be written.
+ *
+ * Per POSIX, the way to write a number is in octal with leading zeroes and
+ * one trailing space (or NUL, but we use space) at the end of the specified
+ * field width.
+ *
+ * However, the given value may not fit in the available space in octal form.
+ * If that's true, we use the GNU extension of writing \200 followed by the
+ * number in base-256 form (ie, stored in binary MSB-first). (Note: here we
+ * support only non-negative numbers, so we don't worry about the GNU rules
+ * for handling negative numbers.)
*/
static void
-print_val(char *s, uint64 val, unsigned int base, size_t len)
+print_tar_number(char *s, int len, uint64 val)
{
- int i;
-
- for (i = len; i > 0; i--)
+ if (val < (((uint64) 1) << ((len - 1) * 3)))
+ {
+ /* Use octal with trailing space */
+ s[--len] = ' ';
+ while (len)
+ {
+ s[--len] = (val & 7) + '0';
+ val >>= 3;
+ }
+ }
+ else
{
- int digit = val % base;
+ /* Use base-256 with leading \200 */
+ s[0] = '\200';
+ while (len > 1)
+ {
+ s[--len] = (val & 255);
+ val >>= 8;
+ }
+ }
+}
- s[i - 1] = '0' + digit;
- val = val / base;
+
+/*
+ * Read a numeric field in a tar header. The field starts at *s and is of
+ * length len.
+ *
+ * The POSIX-approved format for a number is octal, ending with a space or
+ * NUL. However, for values that don't fit, we recognize the GNU extension
+ * of \200 followed by the number in base-256 form (ie, stored in binary
+ * MSB-first). (Note: here we support only non-negative numbers, so we don't
+ * worry about the GNU rules for handling negative numbers.)
+ */
+uint64
+read_tar_number(const char *s, int len)
+{
+ uint64 result = 0;
+
+ if (*s == '\200')
+ {
+ /* base-256 */
+ while (--len)
+ {
+ result <<= 8;
+ result |= (unsigned char) (*++s);
+ }
}
+ else
+ {
+ /* octal */
+ while (len-- && *s >= '0' && *s <= '7')
+ {
+ result <<= 3;
+ result |= (*s - '0');
+ s++;
+ }
+ }
+ return result;
}
@@ -46,12 +105,12 @@ tarChecksum(char *header)
/*
* Fill in the buffer pointed to by h with a tar format header. This buffer
- * must always have space for 512 characters, which is a requirement by
+ * must always have space for 512 characters, which is a requirement of
* the tar format.
*/
enum tarError
tarCreateHeader(char *h, const char *filename, const char *linktarget,
- size_t size, mode_t mode, uid_t uid, gid_t gid, time_t mtime)
+ pgoff_t size, mode_t mode, uid_t uid, gid_t gid, time_t mtime)
{
if (strlen(filename) > 99)
return TAR_NAME_TOO_LONG;
@@ -59,12 +118,6 @@ tarCreateHeader(char *h, const char *filename, const char *linktarget,
if (linktarget && strlen(linktarget) > 99)
return TAR_SYMLINK_TOO_LONG;
- /*
- * 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, 512); /* assume tar header size */
/* Name 100 */
@@ -84,46 +137,49 @@ tarCreateHeader(char *h, const char *filename, const char *linktarget,
}
/* Mode 8 - this doesn't include the file type bits (S_IFMT) */
- sprintf(&h[100], "%07o ", (int) (mode & 07777));
+ print_tar_number(&h[100], 8, (mode & 07777));
/* User ID 8 */
- sprintf(&h[108], "%07o ", (int) uid);
+ print_tar_number(&h[108], 8, uid);
/* Group 8 */
- sprintf(&h[116], "%07o ", (int) gid);
+ print_tar_number(&h[116], 8, gid);
- /* File size 12 - 11 digits, 1 space; use print_val for 64 bit support */
+ /* File size 12 */
if (linktarget != NULL || S_ISDIR(mode))
/* Symbolic link or directory has size zero */
- print_val(&h[124], 0, 8, 11);
+ print_tar_number(&h[124], 12, 0);
else
- print_val(&h[124], size, 8, 11);
- sprintf(&h[135], " ");
+ print_tar_number(&h[124], 12, size);
/* Mod Time 12 */
- sprintf(&h[136], "%011o ", (int) mtime);
+ print_tar_number(&h[136], 12, mtime);
/* Checksum 8 cannot be calculated until we've filled all other fields */
if (linktarget != NULL)
{
/* Type - Symbolic link */
- sprintf(&h[156], "2");
+ h[156] = '2';
/* Link Name 100 */
strlcpy(&h[157], linktarget, 100);
}
else if (S_ISDIR(mode))
+ {
/* Type - directory */
- sprintf(&h[156], "5");
+ h[156] = '5';
+ }
else
+ {
/* Type - regular file */
- sprintf(&h[156], "0");
+ h[156] = '0';
+ }
/* Magic 6 */
- sprintf(&h[257], "ustar");
+ strcpy(&h[257], "ustar");
/* Version 2 */
- sprintf(&h[263], "00");
+ memcpy(&h[263], "00", 2);
/* User 32 */
/* XXX: Do we need to care about setting correct username? */
@@ -134,19 +190,15 @@ tarCreateHeader(char *h, const char *filename, const char *linktarget,
strlcpy(&h[297], "postgres", 32);
/* Major Dev 8 */
- sprintf(&h[329], "%07o ", 0);
+ print_tar_number(&h[329], 8, 0);
/* Minor Dev 8 */
- sprintf(&h[337], "%07o ", 0);
+ print_tar_number(&h[337], 8, 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));
+ /* Finally, compute and insert the checksum */
+ print_tar_number(&h[148], 8, tarChecksum(h));
return TAR_OK;
}