path-util.c revision bcb4e51a409d94ae670de96afb8483a4f7855294
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "lib.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "str.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "path-util.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include <unistd.h>
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include <sys/types.h>
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include <sys/stat.h>
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#define PATH_UTIL_MAX_PATH 8*1024
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#define PATH_UTIL_MAX_SYMLINKS 80
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainenstatic int t_getcwd_noalloc(char **dir_r, size_t *asize_r,
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen const char **error_r) ATTR_NULL(2)
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen{
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* @UNSAFE */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen char *dir;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen size_t asize = 128;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen dir = t_buffer_get(asize);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen while (getcwd(dir, asize) == NULL) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (errno != ERANGE) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *error_r = t_strdup_printf("getcwd() failed: %m");
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen asize = nearest_power(asize+1);
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen dir = t_buffer_get(asize);
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (asize_r != NULL)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *asize_r = asize;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *dir_r = dir;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return 0;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenstatic int path_normalize(const char *path, bool resolve_links,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen const char **npath_r, const char **error_r)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen /* @UNSAFE */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen unsigned int link_count = 0;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen char *npath, *npath_pos;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen const char *p;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen size_t asize;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert(path != NULL);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert(npath_r != NULL);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert(error_r != NULL);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (path[0] != '/') {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* relative; initialize npath with current directory */
9398c0935613ba038cf2275ff66c43b25092cfd0Timo Sirainen if (t_getcwd_noalloc(&npath, &asize, error_r) < 0)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen npath_pos = npath + strlen(npath);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert(npath[0] == '/');
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen } else {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* absolute; initialize npath with root */
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen asize = 128;
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen npath = t_buffer_get(asize);
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen npath[0] = '/';
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen npath_pos = npath + 1;
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen }
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen p = path;
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen while (*p != '\0') {
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen struct stat st;
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen ptrdiff_t seglen;
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen const char *segend;
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen /* skip duplicate slashes */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen while (*p == '/')
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen p++;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* find end of path segment */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen for (segend = p; *segend != '\0' && *segend != '/'; segend++);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (segend == p)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen break; /* '\0' */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen seglen = segend - p;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (seglen == 1 && p[0] == '.') {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* a reference to this segment; nothing to do */
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen } else if (seglen == 2 && p[0] == '.' && p[1] == '.') {
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen /* a reference to parent segment; back up to previous
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen * slash */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert(npath_pos >= npath);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if ((npath_pos - npath) > 1) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (*(npath_pos-1) == '/')
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen npath_pos--;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen for (; *(npath_pos-1) != '/'; npath_pos--);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen } else {
3d370bb6763ac4af4a0d143ad7c93300d5ddff89Timo Sirainen /* allocate space if necessary */
3d370bb6763ac4af4a0d143ad7c93300d5ddff89Timo Sirainen i_assert(npath_pos >= npath);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if ((size_t)((npath_pos - npath) + seglen + 1) >= asize) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ptrdiff_t npath_offset = npath_pos - npath;
ce89e2964b6bc4925d2dd690417200a110d041c5Timo Sirainen asize = nearest_power(npath_offset + seglen + 2);
d89def103cc172eac305e0fb733e89f11dae40b5Timo Sirainen npath = t_buffer_reget(npath, asize);
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen npath_pos = npath + npath_offset;
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen }
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen
d89def103cc172eac305e0fb733e89f11dae40b5Timo Sirainen /* make sure npath now ends in slash */
d89def103cc172eac305e0fb733e89f11dae40b5Timo Sirainen i_assert(npath_pos > npath);
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen if (*(npath_pos-1) != '/') {
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen i_assert((size_t)((npath_pos - npath) + 1) < asize);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *(npath_pos++) = '/';
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen /* copy segment to normalized path */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen i_assert(npath_pos >= npath);
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen i_assert((size_t)((npath_pos - npath) + seglen) < asize);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen memmove(npath_pos, p, seglen);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen npath_pos += seglen;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (resolve_links) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* stat path up to here (segend points to tail) */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *npath_pos = '\0';
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (lstat(npath, &st) < 0) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *error_r = t_strdup_printf("lstat() failed: %m");
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen if (S_ISLNK (st.st_mode)) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* symlink */
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen char *npath_link;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen size_t lsize = 128, tlen = strlen(segend), espace;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen size_t ltlen = (link_count == 0 ? 0 : tlen);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen ssize_t ret;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* limit link dereferences */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (++link_count > PATH_UTIL_MAX_SYMLINKS) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen errno = ELOOP;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *error_r = "Too many symlink dereferences";
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* allocate space for preserving tail of previous symlink and
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen first attempt at reading symlink with room for the tail
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen buffer will look like this:
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen [npath][0][preserved tail][link buffer][room for tail][0]
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen */
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen espace = ltlen + tlen + 2;
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen i_assert(npath_pos >= npath);
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen if ((size_t)((npath_pos - npath) + espace + lsize) >= asize) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen ptrdiff_t npath_offset = npath_pos - npath;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen asize = nearest_power((npath_offset + espace + lsize) + 1);
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen lsize = asize - (npath_offset + espace);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen npath = t_buffer_reget(npath, asize);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen npath_pos = npath + npath_offset;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen if (ltlen > 0) {
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen /* preserve tail just after end of npath */
b321df9603081896b70ec44635af96d674a9839aTimo Sirainen i_assert(npath_pos >= npath);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert((size_t)((npath_pos + 1 - npath) + ltlen) < asize);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen memmove(npath_pos + 1, segend, ltlen);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* read the symlink after the preserved tail */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen for (;;) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen npath_link = (npath_pos + 1) + ltlen;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert(npath_link >= npath_pos);
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen i_assert((size_t)((npath_link - npath) + lsize) < asize);
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen /* attempt to read the link */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if ((ret=readlink(npath, npath_link, lsize)) < 0) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *error_r = t_strdup_printf("readlink() failed: %m");
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if ((size_t)ret < lsize) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* POSIX doesn't guarantee the presence of a NIL */
3d370bb6763ac4af4a0d143ad7c93300d5ddff89Timo Sirainen npath_link[ret] = '\0';
0af3274706d337b2930bd34f0377f2cc2dbcd18aTimo Sirainen break;
0af3274706d337b2930bd34f0377f2cc2dbcd18aTimo Sirainen }
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen /* sum of new symlink content length
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen * and path tail length may not
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen exceed maximum */
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen if ((size_t)(ret + tlen) >= PATH_UTIL_MAX_PATH) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen errno = ENAMETOOLONG;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *error_r = "Resulting path is too long";
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen }
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen /* try again with bigger buffer,
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen we need to allocate more space as well if lsize == ret,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen because the returned link may have gotten truncated */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen espace = ltlen + tlen + 2;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert(npath_pos >= npath);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if ((size_t)((npath_pos - npath) + espace + lsize) >= asize ||
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen lsize == (size_t)ret) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ptrdiff_t npath_offset = npath_pos - npath;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen asize = nearest_power((npath_offset + espace + lsize) + 1);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen lsize = asize - (npath_offset + espace);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen npath = t_buffer_reget(npath, asize);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen npath_pos = npath + npath_offset;
e2a700d0628e395d64cbcef4b5b4510816bf51c4Timo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen }
e2a700d0628e395d64cbcef4b5b4510816bf51c4Timo Sirainen
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* add tail of previous path at end of symlink */
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen i_assert(npath_link >= npath);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen if (ltlen > 0) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen i_assert(npath_pos >= npath);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen i_assert((size_t)((npath_pos - npath) + 1 + tlen) < asize);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen i_assert((size_t)((npath_link - npath) + ret + tlen) < asize);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen memcpy(npath_link + ret, npath_pos + 1, tlen);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen } else {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert((size_t)((npath_link - npath) + ret + tlen) < asize);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen memcpy(npath_link + ret, segend, tlen);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen *(npath_link+ret+tlen) = '\0';
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* use as new source path */
2efe0618b62fb1e3cd41a692f02d674a54c7720eTimo Sirainen path = segend = npath_link;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen if (path[0] == '/') {
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen /* absolute symlink; start over at root */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen npath_pos = npath + 1;
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen } else {
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen /* relative symlink; back up to previous segment */
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen i_assert(npath_pos >= npath);
e2a700d0628e395d64cbcef4b5b4510816bf51c4Timo Sirainen if ((npath_pos - npath) > 1) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen if (*(npath_pos-1) == '/')
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen npath_pos--;
e2a700d0628e395d64cbcef4b5b4510816bf51c4Timo Sirainen for (; *(npath_pos-1) != '/'; npath_pos--);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen } else if (*segend != '\0' && !S_ISDIR (st.st_mode)) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* not last segment, but not a directory either */
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen errno = ENOTDIR;
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen *error_r = t_strdup_printf("Not a directory: %s", npath);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen }
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen p = segend;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen }
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(npath_pos >= npath);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert((size_t)(npath_pos - npath) < asize);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen /* remove any trailing slash */
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if ((npath_pos - npath) > 1 && *(npath_pos-1) == '/')
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen npath_pos--;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen *npath_pos = '\0';
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen t_buffer_alloc(npath_pos - npath + 1);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen *npath_r = npath;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen return 0;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen}
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainenint t_normpath(const char *path, const char **npath_r, const char **error_r)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen{
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen return path_normalize(path, FALSE, npath_r, error_r);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen}
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainenint t_normpath_to(const char *path, const char *root, const char **npath_r,
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen const char **error_r)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen{
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(path != NULL);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(root != NULL);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(npath_r != NULL);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if (*path == '/')
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen return t_normpath(path, npath_r, error_r);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen return t_normpath(t_strconcat(root, "/", path, NULL), npath_r, error_r);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen}
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainenint t_realpath(const char *path, const char **npath_r, const char **error_r)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen{
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen return path_normalize(path, TRUE, npath_r, error_r);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen}
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainenint t_realpath_to(const char *path, const char *root, const char **npath_r,
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen const char **error_r)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen{
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(path != NULL);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(root != NULL);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(npath_r != NULL);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if (*path == '/')
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen return t_realpath(path, npath_r, error_r);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen return t_realpath(t_strconcat(root, "/", path, NULL), npath_r, error_r);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen}
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainenint t_abspath(const char *path, const char **abspath_r, const char **error_r)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen{
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(path != NULL);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(abspath_r != NULL);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(error_r != NULL);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if (*path == '/') {
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen *abspath_r = path;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen return 0;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen const char *dir, *error;
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen if (t_get_working_dir(&dir, &error) < 0) {
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen *error_r = t_strconcat("Failed to get working directory: ",
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen error, NULL);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *abspath_r = t_strconcat(dir, "/", path, NULL);
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen return 0;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainenconst char *t_abspath_to(const char *path, const char *root)
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen{
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen i_assert(path != NULL);
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen i_assert(root != NULL);
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen if (*path == '/')
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen return path;
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen return t_strconcat(root, "/", path, NULL);
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen}
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenint t_get_working_dir(const char **dir_r, const char **error_r)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen char *dir;
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen i_assert(dir_r != NULL);
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen i_assert(error_r != NULL);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (t_getcwd_noalloc(&dir, NULL, error_r) < 0)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen t_buffer_alloc(strlen(dir) + 1);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *dir_r = dir;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return 0;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenint t_readlink(const char *path, const char **dest_r, const char **error_r)
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen{
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen i_assert(error_r != NULL);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* @UNSAFE */
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen ssize_t ret;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen char *dest;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen size_t size = 128;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen dest = t_buffer_get(size);
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen while ((ret = readlink(path, dest, size)) >= (ssize_t)size) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen size = nearest_power(size+1);
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen dest = t_buffer_get(size);
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen }
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen if (ret < 0) {
bd354c19cb93c07ade79477674328a54146ea332Timo Sirainen *error_r = t_strdup_printf("readlink() failed: %m");
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen return -1;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen dest[ret] = '\0';
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen t_buffer_alloc(ret + 1);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen *dest_r = dest;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen return 0;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen}
b42f37ae6f65ed986315b6885568d32115e589b1Timo Sirainen
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainenbool t_binary_abspath(const char **binpath, const char **error_r)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen const char *path_env, *const *paths;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen string_t *path;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (**binpath == '/') {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* already have absolute path */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return TRUE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen } else if (strchr(*binpath, '/') != NULL) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* relative to current directory */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen const char *error;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (t_abspath(*binpath, binpath, &error) < 0) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *error_r = t_strdup_printf("t_abspath(%s) failed: %s",
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *binpath, error);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return FALSE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return TRUE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen } else if ((path_env = getenv("PATH")) != NULL) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* we have to find our executable from path */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen path = t_str_new(256);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen paths = t_strsplit(path_env, ":");
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen for (; *paths != NULL; paths++) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen str_append(path, *paths);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen str_append_c(path, '/');
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen str_append(path, *binpath);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (access(str_c(path), X_OK) == 0) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *binpath = str_c(path);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return TRUE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen str_truncate(path, 0);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *error_r = "Could not find the wanted executable from PATH";
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen return FALSE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen } else {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *error_r = "PATH environment variable undefined";
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return FALSE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen