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>