ar: use strrchr for member filenames

Replace the single-use basename helper with strrchr() and preserve the
existing rejection of pathnames ending in a slash.

Cache the member filename length to avoid repeated strlen() calls while
building and writing the archive header.
This commit is contained in:
datauwu committed 2026-07-19 18:01:44 +08:00
1 parent e9fcf44e02
commit 70a04a84cc
1 file changed
+26 -47
+26 -47
View File
@@ -40,6 +40,7 @@
#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_string.h"
#include "archive_write_private.h"
#include "archive_write_set_format_private.h"
@@ -78,7 +79,6 @@ static ssize_t archive_write_ar_data(struct archive_write *,
static int archive_write_ar_free(struct archive_write *);
static int archive_write_ar_close(struct archive_write *);
static int archive_write_ar_finish_entry(struct archive_write *);
static const char *ar_basename(const char *path);
static int format_octal(int64_t v, char *p, int s);
static int format_decimal(int64_t v, char *p, int s);
@@ -147,16 +147,19 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
{
int ret, append_fn;
char buff[60];
char *ss, *se;
char *ss;
struct ar_w *ar;
struct archive_string se;
const char *pathname;
const char *filename;
size_t filename_length;
int64_t size;
append_fn = 0;
ar = (struct ar_w *)a->format_data;
ar->is_strtab = 0;
filename = NULL;
filename_length = 0;
size = archive_entry_size(entry);
@@ -215,12 +218,17 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
* Otherwise, entry is a normal archive member.
* Strip leading paths from filenames, if any.
*/
if ((filename = ar_basename(pathname)) == NULL) {
filename = strrchr(pathname, '/');
if (filename == NULL)
filename = pathname;
else if (filename[1] == '\0') {
/* Reject filenames with trailing "/" */
archive_set_error(&a->archive, EINVAL,
"Invalid filename");
return (ARCHIVE_WARN);
}
} else
filename++;
filename_length = strlen(filename);
if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) {
/*
@@ -229,10 +237,10 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
* So, the longest filename here (without extension) is
* actually 15 bytes.
*/
if (strlen(filename) <= 15) {
if (filename_length <= 15) {
memcpy(&buff[AR_name_offset],
filename, strlen(filename));
buff[AR_name_offset + strlen(filename)] = '/';
filename, filename_length);
buff[AR_name_offset + filename_length] = '/';
} else {
/*
* For filename longer than 15 bytes, GNU variant
@@ -246,18 +254,10 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
return (ARCHIVE_WARN);
}
se = malloc(strlen(filename) + 3);
if (se == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate filename buffer");
return (ARCHIVE_FATAL);
}
memcpy(se, filename, strlen(filename));
strcpy(se + strlen(filename), "/\n");
ss = strstr(ar->strtab, se);
free(se);
archive_string_init(&se);
archive_string_sprintf(&se, "%s/\n", filename);
ss = strstr(ar->strtab, se.s);
archive_string_free(&se);
if (ss == NULL) {
archive_set_error(&a->archive, EINVAL,
@@ -289,13 +289,13 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
* The name is then written immediately following the
* archive header.
*/
if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) {
memcpy(&buff[AR_name_offset], filename, strlen(filename));
buff[AR_name_offset + strlen(filename)] = ' ';
if (filename_length <= 16 && strchr(filename, ' ') == NULL) {
memcpy(&buff[AR_name_offset], filename, filename_length);
buff[AR_name_offset + filename_length] = ' ';
}
else {
memcpy(buff + AR_name_offset, "#1/", 3);
if (format_decimal(strlen(filename),
if (format_decimal(filename_length,
buff + AR_name_offset + 3,
AR_name_size - 3)) {
archive_set_error(&a->archive, ERANGE,
@@ -303,7 +303,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
return (ARCHIVE_WARN);
}
append_fn = 1;
size += strlen(filename);
size += filename_length;
}
}
@@ -353,10 +353,10 @@ size:
ar->entry_padding = ar->entry_bytes_remaining % 2;
if (append_fn > 0) {
ret = __archive_write_output(a, filename, strlen(filename));
ret = __archive_write_output(a, filename, filename_length);
if (ret != ARCHIVE_OK)
return (ret);
ar->entry_bytes_remaining -= strlen(filename);
ar->entry_bytes_remaining -= filename_length;
}
return (ARCHIVE_OK);
@@ -546,24 +546,3 @@ format_decimal(int64_t v, char *p, int s)
return (-1);
}
static const char *
ar_basename(const char *path)
{
const char *endp, *startp;
endp = path + strlen(path) - 1;
/*
* For filename with trailing slash(es), we return
* NULL indicating an error.
*/
if (*endp == '/')
return (NULL);
/* Find the start of the base */
startp = endp;
while (startp > path && *(startp - 1) != '/')
startp--;
return (startp);
}