install: Simplify path construction.

There's no need to copy the path twice to split it into base and dir.
We simply call `basename()` first, then handle the two trivial cases in
which it isn't safe to call `dirname()`.

While here, add an early check that the destination is not an empty
string.  This would always fail eventually, so it may as well fail
right away.  Also add a test case for this shortcut.

MFC after:	1 week
Sponsored by:	Klara, Inc.
Reviewed by:	markj
Differential Revision:	https://reviews.freebsd.org/D44743
This commit is contained in:
Dag-Erling Smørgrav 2024-04-12 19:30:52 +02:00
parent e5035d0857
commit 17dc7017d7
2 changed files with 29 additions and 13 deletions

View File

@ -25,6 +25,13 @@
# #
# #
atf_test_case copy_to_empty
copy_to_empty_body() {
printf 'test\n123\r456\r\n789\0z' >testf
atf_check -s not-exit:0 -e match:"empty string" \
install testf ""
}
copy_to_nonexistent_with_opts() { copy_to_nonexistent_with_opts() {
printf 'test\n123\r456\r\n789\0z' >testf printf 'test\n123\r456\r\n789\0z' >testf
atf_check install "$@" testf copyf atf_check install "$@" testf copyf
@ -497,6 +504,7 @@ set_optional_exec_body()
} }
atf_init_test_cases() { atf_init_test_cases() {
atf_add_test_case copy_to_empty
atf_add_test_case copy_to_nonexistent atf_add_test_case copy_to_nonexistent
atf_add_test_case copy_to_nonexistent_safe atf_add_test_case copy_to_nonexistent_safe
atf_add_test_case copy_to_nonexistent_comparing atf_add_test_case copy_to_nonexistent_comparing

View File

@ -657,8 +657,10 @@ static void
makelink(const char *from_name, const char *to_name, makelink(const char *from_name, const char *to_name,
const struct stat *target_sb) const struct stat *target_sb)
{ {
char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN]; char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN];
struct stat to_sb; char *to_name_copy, *d, *ld, *ls, *s;
const char *base, *dir;
struct stat to_sb;
/* Try hard links first. */ /* Try hard links first. */
if (dolink & (LN_HARD|LN_MIXED)) { if (dolink & (LN_HARD|LN_MIXED)) {
@ -719,8 +721,6 @@ makelink(const char *from_name, const char *to_name,
} }
if (dolink & LN_RELATIVE) { if (dolink & LN_RELATIVE) {
char *to_name_copy, *cp, *d, *ld, *ls, *s;
if (*from_name != '/') { if (*from_name != '/') {
/* this is already a relative link */ /* this is already a relative link */
do_symlink(from_name, to_name, target_sb); do_symlink(from_name, to_name, target_sb);
@ -740,17 +740,23 @@ makelink(const char *from_name, const char *to_name,
to_name_copy = strdup(to_name); to_name_copy = strdup(to_name);
if (to_name_copy == NULL) if (to_name_copy == NULL)
err(EX_OSERR, "%s: strdup", to_name); err(EX_OSERR, "%s: strdup", to_name);
cp = dirname(to_name_copy); base = basename(to_name_copy);
if (realpath(cp, dst) == NULL) if (base == to_name_copy) {
err(EX_OSERR, "%s: realpath", cp); /* destination is a file in cwd */
/* .. and add the last component. */ (void)strlcpy(dst, "./", sizeof(dst));
if (strcmp(dst, "/") != 0) { } else if (base == to_name_copy + 1) {
if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst)) /* destination is a file in the root */
(void)strlcpy(dst, "/", sizeof(dst));
} else {
/* all other cases: safe to call dirname() */
dir = dirname(to_name_copy);
if (realpath(dir, dst) == NULL)
err(EX_OSERR, "%s: realpath", dir);
if (strcmp(dst, "/") != 0 &&
strlcat(dst, "/", sizeof(dst)) > sizeof(dst))
errx(1, "resolved pathname too long"); errx(1, "resolved pathname too long");
} }
strcpy(to_name_copy, to_name); if (strlcat(dst, base, sizeof(dst)) > sizeof(dst))
cp = basename(to_name_copy);
if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst))
errx(1, "resolved pathname too long"); errx(1, "resolved pathname too long");
free(to_name_copy); free(to_name_copy);
@ -834,6 +840,8 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
} else { } else {
devnull = 1; devnull = 1;
} }
if (*to_name == '\0')
errx(EX_USAGE, "destination cannot be an empty string");
target = (lstat(to_name, &to_sb) == 0); target = (lstat(to_name, &to_sb) == 0);