tar: archive: improve fix for long names crashing - sbase - suckless unix tools
 (HTM) git clone git://git.suckless.org/sbase
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 39a4c55378294437627421571a51b64bd5e09623
 (DIR) parent 97629ab38692ee65250a882bb88eb31c71e51f00
 (HTM) Author: Andrea Calligaris <ac89.hk.public@gmail.com>
       Date:   Wed, 26 Feb 2025 11:03:58 +0100
       
       tar: archive: improve fix for long names crashing
       
       As requested, I resend my old patch for fixing the crashing while
       archiving with names longer than 100 characters.
       
       Last patch dealing with the issue was [1], and the old patch was [2]. The
       code before this commit was not dealing correctly with multiple slashes,
       but use of basename(3) and dirname(3) needed a temporary buffer because
       otherwise we destroyed the path that was used later in several places.
       This solution does not modifies the path and use pointer arithmetic to
       solve the problem.
       
       [1] https://lists.suckless.org/hackers/2412/19213.html
       [2] https://lists.suckless.org/hackers/2402/19071.html
       
       Co-authored-by: Roberto E. Vargas Caballer <k0ga@shike2.net>
       
       Diffstat:
         M tar.c                               |      44 +++++++++++++++++--------------
       
       1 file changed, 24 insertions(+), 20 deletions(-)
       ---
 (DIR) diff --git a/tar.c b/tar.c
       @@ -180,11 +180,12 @@ static int
        archive(const char *path)
        {
                char b[BLKSIZ];
       +        const char *base, *p;
                struct group *gr;
                struct header *h;
                struct passwd *pw;
                struct stat st;
       -        size_t chksum, i;
       +        size_t chksum, i, nlen, plen;
                ssize_t l, r;
                int fd = -1;
        
       @@ -202,27 +203,27 @@ archive(const char *path)
                h = (struct header *)b;
                memset(b, 0, sizeof(b));
        
       -        if (strlen(path) > 255) {
       -                const char *reason = "path exceeds 255 character limit";
       -                eprintf("malformed tar archive: %s\n", reason);
       -        } else if (strlen(path) >= 100) {
       -                size_t prefix_len = 155;
       -                const char *last_slash = strrchr(path, '/');
       -
       -                if (last_slash && last_slash < path + prefix_len) {
       -                    prefix_len = last_slash - path + 1;
       -                }
       -
       -                /* strlcpy is fine here - for path ONLY -,
       -                 * since we're splitting the path.
       -                 * It's not an issue if the prefix can't hold
       -                 * the full path — name will take the rest. */
       -                strlcpy(h->prefix, path, prefix_len);
       -                estrlcpy(h->name, path + prefix_len, sizeof(h->name));
       -        } else {
       -                estrlcpy(h->name, path,                sizeof(h->name));
       +        plen = 0;
       +        base = path;
       +        if ((nlen = strlen(base)) >= sizeof(h->name)) {
       +                /*
       +                 * Cover case where path name is too long (in which case we
       +                 * need to split it to prefix and name).
       +                 */
       +                if ((base = strrchr(path, '/')) == NULL)
       +                        goto too_long;
       +                for (p = base++; p > path && *p == '/'; --p)
       +                        ;
       +
       +                nlen -= base - path;
       +                plen = p - path + 1;
       +                if (nlen >= sizeof(h->name) || plen >= sizeof(h->prefix))
       +                        goto too_long;
                }
        
       +        memcpy(h->name, base, nlen);
       +        memcpy(h->prefix, path, plen);
       +
                putoctal(h->mode,    (unsigned)st.st_mode & 0777, sizeof(h->mode));
                putoctal(h->uid,     (unsigned)st.st_uid,         sizeof(h->uid));
                putoctal(h->gid,     (unsigned)st.st_gid,         sizeof(h->gid));
       @@ -270,6 +271,9 @@ archive(const char *path)
                }
        
                return 0;
       +
       +too_long:
       +        eprintf("filename too long: %s\n", path);
        }
        
        static int