From 671ea40f626ff98064442834e386a49b009b144d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Wed, 26 May 2021 19:03:47 +0200 Subject: [PATCH] freebsd/libzfs: import execvPe() from FreeBSD 13 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It allocates less and properly deals with argv={NULL} With minor cosmetic changes to match cstyle, remove whitespace damage, and restore direct string printing Reviewed-by: Brian Behlendorf Reviewed-by: Ryan Moeller Reviewed-by: Tony Nguyen Signed-off-by: Ahelenia ZiemiaƄska Closes #12051 --- lib/libzfs/os/freebsd/libzfs_compat.c | 61 ++++++++++++++++++--------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/lib/libzfs/os/freebsd/libzfs_compat.c b/lib/libzfs/os/freebsd/libzfs_compat.c index 0e8a3b1217..3c5f684c68 100644 --- a/lib/libzfs/os/freebsd/libzfs_compat.c +++ b/lib/libzfs/os/freebsd/libzfs_compat.c @@ -52,8 +52,8 @@ execvPe(const char *name, const char *path, char * const *argv, const char **memp; size_t cnt, lp, ln; int eacces, save_errno; - char *cur, buf[MAXPATHLEN]; - const char *p, *bp; + char buf[MAXPATHLEN]; + const char *bp, *np, *op, *p; struct stat sb; eacces = 0; @@ -61,7 +61,7 @@ execvPe(const char *name, const char *path, char * const *argv, /* If it's an absolute or relative path name, it's easy. */ if (strchr(name, '/')) { bp = name; - cur = NULL; + op = NULL; goto retry; } bp = buf; @@ -72,23 +72,30 @@ execvPe(const char *name, const char *path, char * const *argv, return (-1); } - cur = alloca(strlen(path) + 1); - if (cur == NULL) { - errno = ENOMEM; - return (-1); - } - strcpy(cur, path); - while ((p = strsep(&cur, ":")) != NULL) { + op = path; + ln = strlen(name); + while (op != NULL) { + np = strchrnul(op, ':'); + /* * It's a SHELL path -- double, leading and trailing colons * mean the current directory. */ - if (*p == '\0') { + if (np == op) { + /* Empty component. */ p = "."; lp = 1; - } else - lp = strlen(p); - ln = strlen(name); + } else { + /* Non-empty component. */ + p = op; + lp = np - op; + } + + /* Advance to the next component or terminate after this. */ + if (*np == '\0') + op = NULL; + else + op = np + 1; /* * If the path is too long complain. This is a possible @@ -118,15 +125,31 @@ retry: (void) execve(bp, argv, envp); case ENOEXEC: for (cnt = 0; argv[cnt]; ++cnt) ; - memp = alloca((cnt + 2) * sizeof (char *)); + + /* + * cnt may be 0 above; always allocate at least + * 3 entries so that we can at least fit "sh", bp, and + * the NULL terminator. We can rely on cnt to take into + * account the NULL terminator in all other scenarios, + * as we drop argv[0]. + */ + memp = alloca(MAX(3, cnt + 2) * sizeof (char *)); if (memp == NULL) { /* errno = ENOMEM; XXX override ENOEXEC? */ goto done; } - memp[0] = "sh"; - memp[1] = bp; - bcopy(argv + 1, memp + 2, cnt * sizeof (char *)); - execve(_PATH_BSHELL, __DECONST(char **, memp), envp); + if (cnt > 0) { + memp[0] = argv[0]; + memp[1] = bp; + bcopy(argv + 1, memp + 2, + cnt * sizeof (char *)); + } else { + memp[0] = "sh"; + memp[1] = bp; + memp[2] = NULL; + } + (void) execve(_PATH_BSHELL, + __DECONST(char **, memp), envp); goto done; case ENOMEM: goto done;