New functions tarHeaderRename() and tarHeaderGetName() are exposed to
store and retrieve the longer filenames.

tarCreateHeader() continues to limit filenames to 99 bytes to preserve
compatability with existing consumers.
---
 src/include/pgtar.h |   2 +
 src/port/tar.c      | 134 ++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 121 insertions(+), 15 deletions(-)

diff --git a/src/include/pgtar.h b/src/include/pgtar.h
index 906db7c..b1c68fc 100644
--- a/src/include/pgtar.h
+++ b/src/include/pgtar.h
@@ -20,4 +20,6 @@ enum tarError
 };
 
 extern 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);
+extern enum tarError tarHeaderRename(char *h, const char *filename);
 extern int     tarChecksum(char *header);
+extern size_t tarHeaderGetName(const char *h, char *buf, size_t buflen);
diff --git a/src/port/tar.c b/src/port/tar.c
index 72fd4e1..23f0201 100644
--- a/src/port/tar.c
+++ b/src/port/tar.c
@@ -45,6 +45,122 @@ tarChecksum(char *header)
 
 
 /*
+ * Split a file path for use in a tar header. The return value is the second
+ * half of the path if a split is required and possible, NULL otherwise.
+ */
+static const char *
+tarSplitName(const char *filename, size_t len)
+{
+       const char *sep;
+
+       if (len <= 99)
+               sep = NULL;
+       else if ((sep = strchr(&filename[len-99], '/')) != NULL)
+               sep++;
+
+       return NULL;
+}
+
+
+/*
+ * Fill in the name and prefix fields of the tar format header pointed to by
+ * h. This should only be used when initially filling out the header.
+ */
+static enum tarError
+tarHeaderSetName(char *h, const char *filename, bool isdir, bool longname)
+{
+       const char *prefix, *base;
+       size_t len, baselen, prelen;
+
+       len = strlen(filename);
+       if (longname)
+               base = tarSplitName(filename, len);
+       else
+               base = NULL;
+       if (base == NULL)
+       {
+               prefix = "";
+               prelen = 0;
+               base = filename;
+               baselen = len;
+       }
+       else
+       {
+               prefix = filename;
+               prelen = (base - filename) - 1;
+               baselen = len - (base - filename);
+       }
+
+       /*
+        * Save room for a trailing slash if this is a directory or symlink to a
+        * directory
+        */
+       if (isdir && base[baselen-1] != '/')
+               baselen++;
+
+       if (baselen > 99 || prelen > 154)
+               return TAR_NAME_TOO_LONG;
+
+       memcpy(&h[345], prefix, prelen);
+       memset(&h[345+prelen], 0, 155 - prelen);
+       memcpy(&h[0], base, baselen);
+       memset(&h[0+baselen], 0, 100 - baselen);
+
+       /*
+        * 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.
+        */
+       if (isdir && base[baselen-1] != '/')
+               h[0+baselen-1] = '/';
+
+       return TAR_OK;
+}
+
+
+/*
+ * Wrapper around tarHeaderSetName() to be used when changing the name in an
+ * existing header.
+ */
+enum tarError
+tarHeaderRename(char *h, const char *filename)
+{
+       size_t                  len;
+       bool                    isdir;
+       enum tarError   err;
+
+       /* If the existing name ends with a slash then this must be preserved */
+       len = strnlen(h, 100);
+       isdir = (len > 0 && h[len-1] == '/');
+
+       err = tarHeaderSetName(h, filename, isdir, true);
+
+       if (err == TAR_OK)
+               /* Recalculate checksum as per tarCreateHeader() */
+               sprintf(&h[148], "%06o ", tarChecksum(h));
+
+       return err;
+}
+
+
+/*
+ * Copy the full pathname from the tar header pointed to by h into
+ * buf. Returns the total length of the path, if this is >= len then the path
+ * has been truncated.
+ */
+size_t
+tarHeaderGetName(const char *h, char *buf, size_t buflen)
+{
+       strlcpy(buf, &h[345], buflen);
+       if (buflen && buf[0] != '\0')
+               strlcat(buf, "/", buflen);
+       strlcat(buf, &h[0], buflen);
+
+       return (strlen(&h[345]) + 1 + strlen(&h[0]));
+}
+
+
+/*
  * 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
  * the tar format.
@@ -68,20 +184,8 @@ tarCreateHeader(char *h, const char *filename, const char 
*linktarget,
        memset(h, 0, 512);                      /* assume tar header size */
 
        /* Name 100 */
-       strlcpy(&h[0], filename, 100);
-       if (linktarget != NULL || S_ISDIR(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';
-       }
+       tarHeaderSetName(h, filename,
+         (linktarget != NULL || S_ISDIR(mode)), false);
 
        /* Mode 8 - this doesn't include the file type bits (S_IFMT)  */
        sprintf(&h[100], "%07o ", (int) (mode & 07777));
@@ -139,7 +243,7 @@ tarCreateHeader(char *h, const char *filename, const char 
*linktarget,
        /* Minor Dev 8 */
        sprintf(&h[337], "%07o ", 0);
 
-       /* Prefix 155 - not used, leave as nulls */
+       /* Prefix 155 - not currently used, leave as nulls */
 
        /*
         * We mustn't overwrite the next field while inserting the checksum.
-- 
2.3.0



-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to