#include <sys/param.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <libsys.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ssp/ssp.h>
static char * __noinline
realpath1(const char *path, char *resolved)
{
struct stat sb;
char *p, *q;
size_t left_len, prev_len, resolved_len, next_token_len;
unsigned symlinks;
ssize_t slen;
char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
symlinks = 0;
if (path[0] == '/') {
resolved[0] = '/';
resolved[1] = '\0';
if (path[1] == '\0')
return (resolved);
resolved_len = 1;
left_len = strlcpy(left, path + 1, sizeof(left));
} else {
if (getcwd(resolved, PATH_MAX) == NULL) {
resolved[0] = '.';
resolved[1] = '\0';
return (NULL);
}
resolved_len = strlen(resolved);
left_len = strlcpy(left, path, sizeof(left));
}
if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
errno = ENAMETOOLONG;
return (NULL);
}
while (left_len != 0) {
p = strchr(left, '/');
next_token_len = p != NULL ? (size_t)(p - left) : left_len;
memcpy(next_token, left, next_token_len);
next_token[next_token_len] = '\0';
if (p != NULL) {
left_len -= next_token_len + 1;
memmove(left, p + 1, left_len + 1);
} else {
left[0] = '\0';
left_len = 0;
}
if (resolved[resolved_len - 1] != '/') {
if (resolved_len + 1 >= PATH_MAX) {
errno = ENAMETOOLONG;
return (NULL);
}
resolved[resolved_len++] = '/';
resolved[resolved_len] = '\0';
}
if (next_token[0] == '\0') {
continue;
} else if (strcmp(next_token, ".") == 0) {
continue;
} else if (strcmp(next_token, "..") == 0) {
if (resolved_len > 1) {
resolved[resolved_len - 1] = '\0';
q = strrchr(resolved, '/') + 1;
*q = '\0';
resolved_len = q - resolved;
}
continue;
}
prev_len = resolved_len;
resolved_len += strlcpy(resolved + prev_len, next_token,
PATH_MAX - prev_len);
if (resolved_len >= PATH_MAX) {
errno = ENAMETOOLONG;
return (NULL);
}
if (lstat(resolved, &sb) != 0) {
if (errno == EACCES || errno == ENOTDIR) {
if (prev_len > 1)
prev_len--;
resolved[prev_len] = '\0';
}
return (NULL);
}
if (S_ISLNK(sb.st_mode)) {
if (symlinks++ > MAXSYMLINKS) {
errno = ELOOP;
return (NULL);
}
slen = readlink(resolved, symlink, sizeof(symlink));
if (slen < 0)
return (NULL);
if (slen == 0) {
errno = ENOENT;
return (NULL);
}
if ((size_t)slen >= sizeof(symlink)) {
errno = ENAMETOOLONG;
return (NULL);
}
symlink[slen] = '\0';
if (symlink[0] == '/') {
resolved[1] = 0;
resolved_len = 1;
} else {
q = strrchr(resolved, '/') + 1;
*q = '\0';
resolved_len = q - resolved;
}
if (p != NULL) {
if (symlink[slen - 1] != '/') {
if ((size_t)slen + 1 >= sizeof(symlink)) {
errno = ENAMETOOLONG;
return (NULL);
}
symlink[slen] = '/';
symlink[slen + 1] = 0;
}
left_len = strlcat(symlink, left,
sizeof(symlink));
if (left_len >= sizeof(symlink)) {
errno = ENAMETOOLONG;
return (NULL);
}
}
left_len = strlcpy(left, symlink, sizeof(left));
} else if (!S_ISDIR(sb.st_mode) && p != NULL) {
errno = ENOTDIR;
return (NULL);
}
}
if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
resolved[resolved_len - 1] = '\0';
return (resolved);
}
char *
__ssp_real(realpath)(const char * __restrict path, char * __restrict resolved)
{
char *m, *res;
if (path == NULL) {
errno = EINVAL;
return (NULL);
}
if (path[0] == '\0') {
errno = ENOENT;
return (NULL);
}
if (resolved != NULL) {
m = NULL;
} else {
m = resolved = malloc(PATH_MAX);
if (resolved == NULL)
return (NULL);
}
if (__sys___realpathat(AT_FDCWD, path, resolved, PATH_MAX, 0) == 0) {
return (resolved);
}
res = realpath1(path, resolved);
if (res == NULL)
free(m);
return (res);
}