[prev in list] [next in list] [prev in thread] [next in thread] 

List:       postgresql-general
Subject:    [HACKERS] [PATCH 1/6] Add support for longer filenames in tar headers (up to 254 bytes).
From:       Joshua Elsasser <josh () idealist ! org>
Date:       2015-09-29 22:16:23
Message-ID: 1443564988-17928-2-git-send-email-josh () idealist ! org
[Download RAW message or body]

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


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic