diff options
author | Bob Beck <beck@cvs.openbsd.org> | 2019-05-28 13:08:57 +0000 |
---|---|---|
committer | Bob Beck <beck@cvs.openbsd.org> | 2019-05-28 13:08:57 +0000 |
commit | 20eca5d46140331c440996fa83a507774a6820bc (patch) | |
tree | 1a284a5ba0a12020a940d53902825c5482b4eb68 | |
parent | f61f9c0fb1539ef0841643d2c7c02a208242786a (diff) |
Enable the use of the kernel __realpath() system call in the libc wrapper.
For now, this also still uses the existing realpath implmentation
and emits a syslog if we see differening results. Once we have run
with that for a little while we will remove the old code
ok deraadt@
-rw-r--r-- | lib/libc/stdlib/realpath.c | 139 | ||||
-rw-r--r-- | lib/libc/sys/Makefile.inc | 4 |
2 files changed, 138 insertions, 5 deletions
diff --git a/lib/libc/stdlib/realpath.c b/lib/libc/stdlib/realpath.c index 5d226d9abc8..76f88562fb0 100644 --- a/lib/libc/stdlib/realpath.c +++ b/lib/libc/stdlib/realpath.c @@ -1,4 +1,4 @@ -/* $OpenBSD: realpath.c,v 1.22 2017/12/24 01:50:50 millert Exp $ */ +/* $OpenBSD: realpath.c,v 1.23 2019/05/28 13:08:56 beck Exp $ */ /* * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru> * @@ -32,6 +32,8 @@ #include <string.h> #include <unistd.h> #include <limits.h> +#include <syslog.h> +#include <stdarg.h> /* A slightly modified copy of this file exists in libexec/ld.so */ @@ -42,8 +44,8 @@ * components. Returns (resolved) on success, or (NULL) on failure, * in which case the path which caused trouble is left in (resolved). */ -char * -realpath(const char *path, char *resolved) +static char * +urealpath(const char *path, char *resolved) { const char *p; char *q; @@ -51,6 +53,7 @@ realpath(const char *path, char *resolved) unsigned symlinks; int serrno, mem_allocated; ssize_t slen; + int trailingslash = 0; char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; if (path == NULL) { @@ -231,3 +234,133 @@ err: free(resolved); return (NULL); } + +/* + * Copyright (c) 2019 Bob Beck <beck@openbsd.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +int __realpath(const char *pathname, char *resolved); +PROTO_NORMAL(__realpath); + +/* + * wrapper for kernel __realpath + */ + +char * +realpath(const char *path, char *resolved) +{ + char pbuf[PATH_MAX], rbuf[PATH_MAX], expected[PATH_MAX]; + struct syslog_data sdata = SYSLOG_DATA_INIT; + int usererrno = 0, kernelerrno = 0, trailingslash = 0, save_errno; + int kernelonly = (getenv("USE_KERNEL_REALPATH") != NULL); + ssize_t i; + + rbuf[0] = pbuf[0] = expected[0] = '\0'; + + if (!kernelonly) { + memset(expected, 0, sizeof(expected)); + if (urealpath(path, expected) == NULL) { + usererrno = errno; + expected[0] = '\0'; + } + } + + if (path == NULL) { + kernelerrno = EINVAL; + goto out; + } + if (path[0] == '\0') { + kernelerrno = ENOENT; + goto out; + } + if (strlcat(pbuf, path, sizeof(pbuf)) >= sizeof(pbuf)) { + kernelerrno = ENAMETOOLONG; + goto out; + } + + if (pbuf[strlen(pbuf) - 1] == '/') + trailingslash = 1; + + if (__realpath(pbuf, rbuf) == -1) + kernelerrno = errno; + + /* + * XXX XXX XXX + * + * The old userland implementation strips trailing slashes. + * According to Dr. POSIX, realpathing "/bsd" should be fine, + * realpathing "/bsd/" should return ENOTDIR. + * + * Similar, but *different* to the above, The old userland + * implementation allows for realpathing "/nonexistent" but + * not "/nonexistent/", Both those should return ENOENT + * according to POSIX. + * + * This hack should go away once we decide to match POSIX. + * which we should as soon as is convenient. + */ + if (kernelerrno == ENOTDIR) { + /* Try again without the trailing slash. */ + kernelerrno = 0; + for (i = strlen(pbuf); i > 1 && pbuf[i - 1] == '/'; i--) + pbuf[i - 1] = '\0'; + rbuf[0] = '\0'; + if (__realpath(pbuf, rbuf) == -1) + kernelerrno = errno; + } + +out: + if (!kernelonly) { + /* syslog if kernel and userland are different */ + save_errno = errno; + if (strcmp(rbuf, expected) != 0 || (usererrno == 0 && + kernelerrno != 0)) + syslog_r(LOG_CRIT | LOG_CONS, &sdata, + "realpath '%s' -> '%s' errno %d, " + "expected '%s' errno %d", path, rbuf, + kernelerrno, expected, usererrno); + errno = save_errno; + + /* use userland result */ + if (usererrno) { + errno = usererrno; + return NULL; + } + else + errno = 0; + if (resolved == NULL) + resolved = strdup(expected); + else if (strlcpy(resolved, expected, PATH_MAX) >= PATH_MAX) { + errno = ENAMETOOLONG; + return NULL; + } + + } else { + /* use kernel result */ + if (kernelerrno) { + errno = kernelerrno; + return NULL; + } + else + errno = 0; + if (resolved == NULL) + resolved = strdup(rbuf); + else if (strlcpy(resolved, rbuf, PATH_MAX) >= PATH_MAX) { + errno = ENAMETOOLONG; + return NULL; + } + } + return (resolved); +} diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 7cc7d93c390..5dd7f1aeb68 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile.inc,v 1.156 2019/01/11 18:46:30 deraadt Exp $ +# $OpenBSD: Makefile.inc,v 1.157 2019/05/28 13:08:56 beck Exp $ # $NetBSD: Makefile.inc,v 1.35 1995/10/16 23:49:07 jtc Exp $ # @(#)Makefile.inc 8.1 (Berkeley) 6/17/93 @@ -109,7 +109,7 @@ PPSEUDO_NOERR=${PSEUDO_NOERR:.o=.po} SPSEUDO_NOERR=${PSEUDO_NOERR:.o=.so} DPSEUDO_NOERR=${PSEUDO_NOERR:.o=.do} -HIDDEN= ___getcwd.o fork.o sigaction.o _ptrace.o ${CANCEL:=.o} +HIDDEN= ___realpath.o ___getcwd.o fork.o sigaction.o _ptrace.o ${CANCEL:=.o} PHIDDEN=${HIDDEN:.o=.po} SHIDDEN=${HIDDEN:.o=.so} DHIDDEN=${HIDDEN:.o=.do} |