tar: archive: improve fix for long names crashing - sbase - suckless unix tools | |
git clone git://git.suckless.org/sbase | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 39a4c55378294437627421571a51b64bd5e09623 | |
parent 97629ab38692ee65250a882bb88eb31c71e51f00 | |
Author: Andrea Calligaris <[email protected]> | |
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 <[email protected]> | |
Diffstat: | |
M tar.c | 44 +++++++++++++++++------------… | |
1 file changed, 24 insertions(+), 20 deletions(-) | |
--- | |
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 |