[PATCH 1/6] Add support for longer filenames in tar headers (up to 254 bytes). - Mailing list pgsql-hackers

From Joshua Elsasser
Subject [PATCH 1/6] Add support for longer filenames in tar headers (up to 254 bytes).
Date
Msg-id 1443564988-17928-2-git-send-email-josh@idealist.org
Whole thread Raw
In response to Re: Add pg_basebackup single tar output format  (Andres Freund <andres@anarazel.de>)
List pgsql-hackers
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
insertingthe checksum.
 
-- 
2.3.0




pgsql-hackers by date:

Previous
From: Stephen Frost
Date:
Subject: Re: unclear about row-level security USING vs. CHECK
Next
From: Joshua Elsasser
Date:
Subject: Re: Add pg_basebackup single tar output format