[klibc] [PATCH 1/2] Implement realpath()

Ben Hutchings ben at decadent.org.uk
Sat Sep 27 07:29:39 PDT 2014


This is needed as the basis for the readlink -f option.

Signed-off-by: Ben Hutchings <ben at decadent.org.uk>
---
--- a/usr/include/stdlib.h
+++ b/usr/include/stdlib.h
@@ -92,4 +92,6 @@ static __inline__ int grantpt(int __fd)
 	return 0;		/* devpts does this all for us! */
 }
 
+__extern char *realpath(const char *, char *);
+
 #endif				/* _STDLIB_H */
--- a/usr/klibc/Kbuild
+++ b/usr/klibc/Kbuild
@@ -60,7 +60,7 @@ klib-y += vsnprintf.o snprintf.o vsprint
 	  send.o recv.o \
 	  access.o chmod.o chown.o dup2.o mknod.o poll.o rename.o stat.o \
 	  lchown.o link.o rmdir.o unlink.o utimes.o lstat.o mkdir.o \
-	  readlink.o select.o symlink.o pipe.o \
+	  readlink.o realpath.o select.o symlink.o pipe.o \
 	  ctype/isalnum.o ctype/isalpha.o ctype/isascii.o \
 	  ctype/isblank.o ctype/iscntrl.o ctype/isdigit.o \
 	  ctype/isgraph.o ctype/islower.o ctype/isprint.o \
--- /dev/null
+++ b/usr/klibc/realpath.c
@@ -0,0 +1,147 @@
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static char *__realpath(const char *name, char *resolved_name, int recurse)
+{
+	char link_target[PATH_MAX];
+	struct stat st;
+	char *p, *end;
+	size_t comp_len;
+	int link_len;
+	int exists = 1;
+	int is_dir = 1;
+
+	/* Keep or ignore base dir depending on whether name is relative */
+	p = resolved_name;
+	if (*name != '/')
+		p += strlen(p);
+
+	/* Find end of buffer */
+	end = resolved_name + PATH_MAX;
+
+	/* Iterate over name components */
+	while (*name) {
+		comp_len = strcspn(name, "/");
+
+		/* Only slashes are allowed after a nonexistent component */
+		if (comp_len != 0 && !exists) {
+			errno = ENOENT;
+			return NULL;
+		}
+
+		if (comp_len == 0 || (comp_len == 1 && *name == '.')) {
+			/* Skip empty or "." */
+		} else if (comp_len == 2 && name[0] == '.' && name[1] == '.') {
+			/* Handle ".." */
+			p = memrchr(resolved_name, '/', p - resolved_name);
+			if (!p)
+				p = resolved_name;
+		} else {
+			/* Add directory separator and component */
+			if (end - p < comp_len + 2) {
+				errno = ENAMETOOLONG;
+				return NULL;
+			}
+			*p = '/';
+			memcpy(p + 1, name, comp_len);
+			p[1 + comp_len] = 0;
+
+			link_len = readlink(resolved_name, link_target,
+					    sizeof(link_target) - 1);
+			if (link_len < 0) {
+				if (errno == EINVAL || errno == ENOENT) {
+					/* Not a symlink - continue */
+					p += 1 + comp_len;
+				} else {
+					/* Couldn't read symlink */
+					return NULL;
+				}
+			} else {
+				/* Limit recursion */
+				if (recurse == 0) {
+					errno = ELOOP;
+					return NULL;
+				}
+
+				/* Replace component with link target */
+				*p = 0;
+				link_target[link_len] = 0;
+				if (!__realpath(link_target, resolved_name,
+						recurse - 1))
+					return NULL;
+				p = resolved_name + strlen(resolved_name);
+			}
+
+			if (lstat(resolved_name, &st)) {
+				if (errno != ENOENT)
+					return NULL;
+				exists = 0;
+			} else if (!S_ISDIR(st.st_mode)) {
+				is_dir = 0;
+			}
+		}
+
+		name += comp_len;
+		if (*name == '/') {
+			/*
+			 * No slashes are allowed after an existing
+			 * non-directory
+			 */
+			if (exists && !is_dir) {
+				errno = ENOTDIR;
+				return NULL;
+			}
+			++name;
+		}
+	}
+
+	*p = 0;
+	return resolved_name;
+}
+
+char *realpath(const char *name, char *resolved_name)
+{
+	int allocated = 0;
+
+	/* Empty components are allowed, but not a completely empty string */
+	if (*name == 0) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	if (!resolved_name) {
+		resolved_name = malloc(PATH_MAX);
+		if (!resolved_name)
+			return NULL;
+		allocated = 1;
+	}
+
+	/* Set base dir to cwd if necessary */
+	if (*name != '/') {
+		if (!getcwd(resolved_name, PATH_MAX)) {
+			if (errno == ERANGE)
+				errno = ENAMETOOLONG;
+			goto fail;
+		}
+
+		/* __realpath() never wants a trailing slash */
+		if (!strcmp(resolved_name, "/"))
+			*resolved_name = 0;
+	}
+
+	if (__realpath(name, resolved_name, 8)) {
+		/* Never return an empty string */
+		if (*resolved_name == 0)
+			strcpy(resolved_name, "/");
+
+		return resolved_name;
+	}
+
+fail:
+	if (allocated)
+		free(resolved_name);
+	return NULL;
+}

-- 
Ben Hutchings
The two most common things in the universe are hydrogen and stupidity.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 811 bytes
Desc: This is a digitally signed message part
URL: <http://www.zytor.com/pipermail/klibc/attachments/20140927/895d6d4d/attachment.sig>


More information about the klibc mailing list