[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: