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>
This is needed as the basis for the readlink -f option.
Signed-off-by: Ben Hutchings <ben at decadent.org.uk>
---
v2: Don't implement the BSD/GNU extension of allowing a non-existent
last part.
Use open(O_PATH) and procfs to get resolved name from the kernel.
--- 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,45 @@
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/*
+ * Note that this requires name to refer to an existing file. This is
+ * correct according to POSIX. However, BSD and GNU implementations
+ * also allow name to refer to a non-existing file in an existing
+ * directory.
+ */
+
+char *realpath(const char *name, char *resolved_name)
+{
+ static const char proc_fd_prefix[] = "/proc/self/fd/";
+ char proc_fd_name[sizeof(proc_fd_prefix) + sizeof(int) * 3];
+ int allocated = 0;
+ int fd;
+ ssize_t len;
+
+ /* Open for path lookup only */
+ fd = open(name, O_PATH);
+ if (fd < 0)
+ return NULL;
+
+ if (!resolved_name) {
+ resolved_name = malloc(PATH_MAX);
+ if (!resolved_name)
+ return NULL;
+ allocated = 1;
+ }
+
+ /* Use procfs to read back the resolved name */
+ sprintf(proc_fd_name, "%s%d", proc_fd_prefix, fd);
+ len = readlink(proc_fd_name, resolved_name, PATH_MAX - 1);
+ if (len < 0) {
+ if (allocated)
+ free(resolved_name);
+ return NULL;
+ }
+
+ resolved_name[len] = 0;
+ return resolved_name;
+}
--
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/20140930/c904bd12/attachment.sig>
This is needed as the basis for the readlink -f option.
Signed-off-by: Ben Hutchings <ben at decadent.org.uk>
---
v2: Don't implement the BSD/GNU extension of allowing a non-existent
last part.
Use open(O_PATH) and procfs to get resolved name from the kernel.
v3: Don't leak the fd.
--- 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,48 @@
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/*
+ * Note that this requires name to refer to an existing file. This is
+ * correct according to POSIX. However, BSD and GNU implementations
+ * also allow name to refer to a non-existing file in an existing
+ * directory.
+ */
+
+char *realpath(const char *name, char *resolved_name)
+{
+ static const char proc_fd_prefix[] = "/proc/self/fd/";
+ char proc_fd_name[sizeof(proc_fd_prefix) + sizeof(int) * 3];
+ int allocated = 0;
+ int fd;
+ ssize_t len;
+
+ /* Open for path lookup only */
+ fd = open(name, O_PATH);
+ if (fd < 0)
+ return NULL;
+
+ if (!resolved_name) {
+ resolved_name = malloc(PATH_MAX);
+ if (!resolved_name)
+ goto out_close;
+ allocated = 1;
+ }
+
+ /* Use procfs to read back the resolved name */
+ sprintf(proc_fd_name, "%s%d", proc_fd_prefix, fd);
+ len = readlink(proc_fd_name, resolved_name, PATH_MAX - 1);
+ if (len < 0) {
+ if (allocated)
+ free(resolved_name);
+ resolved_name = NULL;
+ } else {
+ resolved_name[len] = 0;
+ }
+
+out_close:
+ close(fd);
+ return resolved_name;
+}
--
Ben Hutchings
One of the nice things about standards is that there are so many of them.
-------------- 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/20141004/96c2d608/attachment.sig>