#include #include #include #include #include #include #include #include "syscall.h" void __procfdname(char *, unsigned); int mergepaths(char *restrict a, const char *restrict r) { size_t i, j; i = j = 0; /* if r is absolute, skip a content */ if (r[0] == '/') { a[0] = '/'; j = 1; } else { /* slash terminate a if needed */ i = strlen(a)-1; if (r[0] && (a[i-1] != '/')) a[i] = '/'; else --i; } /* resolve . and .. */ for (; r[j]; ++j) { if (r[j] == '.') { if (r[j+1] == '/' || !r[j+1]) continue; if (r[j+1] == '.' && (r[j+2] == '/' || !r[j+2])) { while (i > 0 && a[--i] != '/'); continue; } } else if (r[j] == '/' && a[i] == '/') continue; a[++i] = r[j]; } /* terminate a */ if (a[i] == '/' && i > 0) --i; a[++i] = 0; return i; } char *realpath(const char *restrict filename, char *restrict resolved) { int fd; ssize_t r; size_t i; struct stat st1, st2; char buf[15+3*sizeof(int)]; char tmp[PATH_MAX], link[PATH_MAX]; if (!filename) { errno = EINVAL; return 0; } fd = sys_open(filename, O_PATH|O_NONBLOCK|O_CLOEXEC); if (fd < 0) return 0; __procfdname(buf, fd); r = readlink(buf, tmp, sizeof tmp - 1); if (r < 0) goto noproc; tmp[r] = 0; fstat(fd, &st1); r = stat(tmp, &st2); if (r<0 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) { if (!r) errno = ELOOP; goto err; } end: __syscall(SYS_close, fd); return resolved ? strcpy(resolved, tmp) : strdup(tmp); noproc: if (*filename != '/') if (syscall(SYS_getcwd, tmp, sizeof(tmp)) < 0) goto err; /* concatenate cwd and target */ r = mergepaths(tmp, filename); if (lstat(tmp, &st1) < 0) goto err; /* resolve link chain if any */ while (S_ISLNK(st1.st_mode)) { if ((r = readlink(tmp, link, sizeof(link))) < 0) goto err; link[r] = 0; /* remove link name */ i = strlen(tmp) - 1; while (i > 0 && tmp[--i] != '/'); tmp[i] = 0; /* merge path and link target */ mergepaths(tmp, link); if (lstat(tmp, &st1) < 0) goto err; } goto end; err: __syscall(SYS_close, fd); return 0; }