mailing list of musl libc
 help / color / mirror / code / Atom feed
* [musl] realpath without procfs -- should be ready for inclusion
@ 2020-11-22 22:56 Rich Felker
  2020-11-23  2:03 ` Alexey Izbyshev
  0 siblings, 1 reply; 20+ messages in thread
From: Rich Felker @ 2020-11-22 22:56 UTC (permalink / raw)
  To: musl

[-- Attachment #1: Type: text/plain, Size: 1218 bytes --]

I put this aside for a while, but realpath without procfs was one of
the things I wanted to get in this release cycle, and I had it
essentially finished back in September. Here's the latest version.
Compared to the earlier draft, it handles // more consistently (//.
and //.. both resolve to //) and expands getcwd only as a final step
if needed, rather than doing it first. In the case where an absolute
symlink is reached via a relative path, this saves a syscall, avoids
repeated absolute path traversal at each step, and avoids spuriously
erroring out with ENAMETOOLONG when the working directory is deeper
than PATH_MAX but the absolute link target has a valid absolute
pathname.

To recap, the motivation for the rewrite without procfs dependency was
having things work before procfs is mounted or in chroot/namespace
where it's intentionally unavailable.

I originally considered keeping the procfs based version and only
using the new one as a fallback, but I discovered there are cases
(involving chroot, namespaces, etc.) where the answer from procfs is
wrong and validating it requires basically the same procedure as
implementing it manually (walking and performing readlink on each path
component).

Rich

[-- Attachment #2: realpath8.c --]
[-- Type: text/plain, Size: 2392 bytes --]

#define _GNU_SOURCE
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

static inline int at_dotdot(const char *end, size_t len)
{
	if (len<2) return 0;
	if (len>2 && end[-3]!='/') return 0;
	return end[-1]=='.' && end[-2]=='.';
}

char *realpath(const char *restrict filename, char *restrict resolved)
{
	char stack[PATH_MAX];
	char buf[resolved ? 1 : PATH_MAX];
	char *output = resolved ? resolved : buf;
	size_t p, q, l, cnt=0;

	l = strnlen(filename, sizeof stack + 1);
	if (!l) {
		errno = ENOENT;
		return 0;
	}
	if (l >= sizeof stack) goto toolong;
	p = sizeof stack - l - 1;
	q = 0;
	memcpy(stack+p, filename, l+1);

	while (stack[p]) {
		int up = 0;
		if (stack[p] == '/') {
			q=0;
			output[q++] = '/';
			p++;
			/* Initial // is special. */
			if (stack[p] == '/' && stack[p+1] != '/') {
				output[q++] = '/';
			}
			while (stack[p] == '/') p++;
			continue;
		}
		char *z = __strchrnul(stack+p, '/');
		l = z-(stack+p);
		if (l==1 && stack[p]=='.') {
			p += l;
			while (stack[p] == '/') p++;
			continue;
		}
		if (at_dotdot(stack+p+l, l)) {
			if (q && !at_dotdot(output+q, q)) {
				while(q && output[q-1]!='/') q--;
				if (q>1 && (q>2 || output[0]!='/')) q--;
				p += l;
				while (stack[p] == '/') p++;
				continue;
			}
			up = 1;
		}
		if (q && output[q-1] != '/') {
			if (!p) goto toolong;
			stack[--p] = '/';
			l++;
		}
		if (q+l >= PATH_MAX) goto toolong;
		memcpy(output+q, stack+p, l);
		output[q+l] = 0;
		p += l;
		if (up) goto notlink;
		ssize_t k = readlink(output, stack, p);
		if (k==-1) {
			if (errno == EINVAL) {
notlink:
				q += l;
				while (stack[p] == '/') p++;
				continue;
			}
			return 0;
		}
		if (k==p) goto toolong;
		if (++cnt == SYMLOOP_MAX) {
			errno = ELOOP;
			return 0;
		}
		p -= k;
		memmove(stack+p, stack, k);
	}

	output[q] = 0;

	if (output[0] != '/') {
		if (!getcwd(stack, sizeof stack)) return 0;
		l = strlen(stack);
		/* Cancel any initial .. components. */
		p = 0;
		while (q-p>=2 && at_dotdot(output+p+2, p+2)) {
			while(l>1 && stack[l-1]!='/') l--;
			if (l>1) l--;
			p += 2;
			if (p<q) p++;
		}
		if (q-p && stack[l-1]!='/') output[--p] = '/';
		if (l + (q-p) + 1 >= PATH_MAX) goto toolong;
		memmove(output + l, output + p, q - p + 1);
		memcpy(output, stack, l);
	}

	return resolved ? resolved : strdup(output);

toolong:
	errno = ENAMETOOLONG;
	return 0;
}

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2020-11-25 19:40 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-22 22:56 [musl] realpath without procfs -- should be ready for inclusion Rich Felker
2020-11-23  2:03 ` Alexey Izbyshev
2020-11-23  3:17   ` Érico Nogueira
2020-11-23  3:34     ` Rich Felker
2020-11-23  3:19   ` Rich Felker
2020-11-23 18:56     ` Rich Felker
2020-11-23 20:53       ` Rich Felker
2020-11-24  3:39         ` Alexey Izbyshev
2020-11-24  4:26           ` Rich Felker
2020-11-24  5:13             ` Alexey Izbyshev
2020-11-24  6:30               ` Rich Felker
2020-11-24  9:21                 ` Alexey Izbyshev
2020-11-24 14:35                   ` Rich Felker
2020-11-24 20:17                     ` Rich Felker
2020-11-25 15:02                   ` Rich Felker
2020-11-25 19:40                     ` Alexey Izbyshev
2020-11-24 20:31             ` Rich Felker
2020-11-25  5:40               ` Alexey Izbyshev
2020-11-25 15:03                 ` Rich Felker
2020-11-24  3:41     ` Alexey Izbyshev

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).