dovecot-2.2-pigeonhole: lib-sieve: Implemented utility functions...
pigeonhole at rename-it.nl
pigeonhole at rename-it.nl
Sat Mar 14 13:49:44 UTC 2015
details: http://hg.rename-it.nl/dovecot-2.2-pigeonhole/rev/163a8fbfdc1a
changeset: 2003:163a8fbfdc1a
user: Stephan Bosch <stephan at rename-it.nl>
date: Thu Mar 12 21:03:28 2015 +0100
description:
lib-sieve: Implemented utility functions to normalize filesystem paths.
diffstat:
src/lib-sieve/util/Makefile.am | 6 +-
src/lib-sieve/util/realpath.c | 237 +++++++++++++++++++++++++++++++++++++++++
src/lib-sieve/util/realpath.h | 33 +++++
3 files changed, 274 insertions(+), 2 deletions(-)
diffs (299 lines):
diff -r 986ea6e9c270 -r 163a8fbfdc1a src/lib-sieve/util/Makefile.am
--- a/src/lib-sieve/util/Makefile.am Wed Mar 11 23:59:51 2015 +0100
+++ b/src/lib-sieve/util/Makefile.am Thu Mar 12 21:03:28 2015 +0100
@@ -12,13 +12,15 @@
rfc2822.c \
program-client-local.c \
program-client-remote.c \
- program-client.c
+ program-client.c \
+ realpath.c
headers = \
edit-mail.h \
rfc2822.h \
program-client-private.h \
- program-client.h
+ program-client.h \
+ realpath.h
pkginc_libdir=$(dovecot_pkgincludedir)/sieve
pkginc_lib_HEADERS = $(headers)
diff -r 986ea6e9c270 -r 163a8fbfdc1a src/lib-sieve/util/realpath.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-sieve/util/realpath.c Thu Mar 12 21:03:28 2015 +0100
@@ -0,0 +1,237 @@
+/* Copyright (c) 2009-2015 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+
+#include "realpath.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// FIXME: move/merge to Dovecot
+
+#define REALPATH_MAX_PATH 8*1024
+#define REALPATH_MAX_SYMLINKS 80
+
+static int t_getcwd_alloc(char **dir_r, size_t *asize_r)
+{
+ /* @UNSAFE */
+ char *dir;
+ size_t asize = 128;
+
+ dir = t_buffer_get(asize);
+ while (getcwd(dir, asize) == NULL) {
+ if (errno != ERANGE)
+ return -1;
+ asize = nearest_power(asize+1);
+ dir = t_buffer_get(asize);
+ }
+ *asize_r = asize;
+ *dir_r = dir;
+ return 0;
+}
+
+static int path_normalize(const char *path, bool resolve_links,
+ const char **npath_r)
+{
+ /* @UNSAFE */
+ unsigned int link_count = 0;
+ char *npath, *npath_pos;
+ const char *p;
+ size_t asize;
+
+ if (path[0] != '/') {
+ /* relative; initialize npath with current directory */
+ if (t_getcwd_alloc(&npath, &asize) < 0)
+ return -1;
+ npath_pos = npath + strlen(npath);
+ i_assert(npath[0] == '/');
+ } else {
+ /* absolute; initialize npath with root */
+ asize = 128;
+ npath = t_buffer_get(asize);
+ npath[0] = '/';
+ npath_pos = npath + 1;
+ }
+
+ p = path;
+ while (*p != '\0') {
+ struct stat st;
+ ptrdiff_t seglen;
+ const char *segend;
+
+ /* skip duplicate shashes */
+ while (*p == '/')
+ p++;
+
+ /* find end of path segment */
+ for (segend = p; *segend != '\0' && *segend != '/'; segend++);
+
+ if (segend == p)
+ break; /* '\0' */
+ seglen = segend - p;
+ if (seglen == 1 && p[0] == '.') {
+ /* a reference to this segment; nothing to do */
+ } else if (seglen == 2 && p[0] == '.' && p[1] == '.') {
+ /* a reference to parent segment; back up to previous slash */
+ if (npath_pos > npath + 1) {
+ if (*npath_pos == '/')
+ npath_pos--;
+ for (; *npath_pos != '/'; npath_pos--);
+ }
+ } else {
+ /* make sure npath now ends in slash */
+ if (*(npath_pos-1) != '/')
+ *(npath_pos++) = '/';
+
+ /* allocate space if necessary */
+ if ((npath_pos + seglen + 1) >= (npath + asize)) {
+ ptrdiff_t npath_offset = npath_pos - npath;
+ asize = nearest_power(npath_offset + seglen + 2);
+ npath = t_buffer_reget(npath, asize);
+ npath_pos = npath + npath_offset;
+ }
+
+ /* copy segment to normalized path */
+ (void)memmove(npath_pos, p, segend - p);
+ npath_pos += seglen;
+ }
+
+ if (resolve_links) {
+ /* stat path up to here (segend points to tail) */
+ *npath_pos = '\0';
+ if (lstat(npath, &st) < 0)
+ return -1;
+
+ if (S_ISLNK (st.st_mode)) {
+ /* symlink */
+ char *npath_link;
+ size_t lsize = 128, tlen = strlen(segend), espace;
+ size_t ltlen = (link_count == 0 ? 0 : tlen);
+ ssize_t ret;
+
+ /* limit link dereferences */
+ if (++link_count > REALPATH_MAX_SYMLINKS) {
+ errno = ELOOP;
+ return -1;
+ }
+
+ /* allocate space for preserving tail of previous symlink and
+ first attempt at reading symlink with room for the tail
+
+ buffer will look like this:
+ [npath][0][preserved tail][link buffer][room for tail][0]
+ */
+ espace = ltlen + tlen + 2;
+ if ((npath_pos + espace + lsize) >= (npath + asize)) {
+ ptrdiff_t npath_offset = npath_pos - npath;
+ asize = nearest_power((npath_offset + espace + lsize) + 1);
+ lsize = asize - (npath_offset + espace);
+ npath = t_buffer_reget(npath, asize);
+ npath_pos = npath + npath_offset;
+ }
+
+ if (ltlen > 0) {
+ /* preserve tail just after end of npath */
+ (void)memmove(npath_pos + 1, segend, ltlen);
+ }
+
+ /* read the symlink after the preserved tail */
+ for (;;) {
+ npath_link = (npath_pos + 1) + ltlen;
+
+ /* attempt to read the link */
+ if ((ret=readlink(npath, npath_link, lsize)) < 0)
+ return -1;
+ if ((size_t)ret < lsize)
+ break;
+
+ /* sum of new symlink content length and path tail length may not
+ exeed maximum */
+ if ((size_t)(ret + tlen) >= REALPATH_MAX_PATH) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ /* try again with bigger buffer */
+ espace = ltlen + tlen + 2;
+ if ((npath_pos + espace + lsize) >= (npath + asize)) {
+ ptrdiff_t npath_offset = npath_pos - npath;
+ asize = nearest_power((npath_offset + espace + lsize) + 1);
+ lsize = asize - (npath_offset + espace);
+ npath = t_buffer_reget(npath, asize);
+ npath_pos = npath + npath_offset;
+ }
+ }
+
+ /* add tail of previous path at end of symlink */
+ if (ltlen > 0)
+ (void)memcpy(npath_link + ret, npath_pos + 1, tlen);
+ else
+ (void)memcpy(npath_link + ret, segend, tlen);
+ *(npath_link+ret+tlen) = '\0';
+
+ /* use as new source path */
+ path = segend = npath_link;
+
+ if (path[0] == '/') {
+ /* absolute symlink; start over at root */
+ npath_pos = npath + 1;
+ } else {
+ /* relative symlink; back up to previous segment */
+ if (npath_pos > npath + 1) {
+ if (*npath_pos == '/')
+ npath_pos--;
+ for (; *npath_pos != '/'; npath_pos--);
+ }
+ }
+
+ } else if (*segend != '\0' && !S_ISDIR (st.st_mode)) {
+ /* not last segment, but not a directory either */
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+
+ p = segend;
+ }
+
+ /* remove any trailing slash */
+ if (npath_pos > npath + 1 && *(npath_pos-1) == '/')
+ npath_pos--;
+ *npath_pos = '\0';
+
+ t_buffer_alloc(npath_pos - npath + 1);
+ *npath_r = npath;
+ return 0;
+}
+
+int t_normpath(const char *path, const char **npath_r)
+{
+ return path_normalize(path, FALSE, npath_r);
+}
+
+int t_normpath_to(const char *path, const char *root,
+ const char **npath_r)
+{
+ if (*path == '/')
+ return t_normpath(path, npath_r);
+
+ return t_normpath(t_strconcat(root, "/", path, NULL), npath_r);
+}
+
+int t_realpath(const char *path, const char **npath_r)
+{
+ return path_normalize(path, TRUE, npath_r);
+}
+
+int t_realpath_to(const char *path, const char *root,
+ const char **npath_r)
+{
+ if (*path == '/')
+ return t_realpath(path, npath_r);
+
+ return t_realpath(t_strconcat(root, "/", path, NULL), npath_r);
+}
diff -r 986ea6e9c270 -r 163a8fbfdc1a src/lib-sieve/util/realpath.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-sieve/util/realpath.h Thu Mar 12 21:03:28 2015 +0100
@@ -0,0 +1,33 @@
+#ifndef REALPATH_H
+#define REALPATH_H
+
+/* Returns path as the normalized absolute path, which means that './'
+ and '../' components are resolved, and that duplicate and trailing
+ slashes are removed. If it's not already the absolute path, it's
+ assumed to be relative to the current working directory.
+
+ NOTE: Be careful with this function. The resolution of '../' components
+ with the parent component as if it were a normal directory is not valid
+ if the path contains symbolic links.
+ */
+int t_normpath(const char *path, const char **npath_r);
+/* Like t_normpath(), but path is relative to given root. */
+int t_normpath_to(const char *path, const char *root,
+ const char **npath_r);
+
+/* Returns path as the real normalized absolute path, which means that all
+ symbolic links in the path are resolved, that './' and '../' components
+ are resolved, and that duplicate and trailing slashes are removed. If it's
+ not already the absolute path, it's assumed to be relative to the current
+ working directory.
+
+ NOTE: This function calls stat() for each path component and more when
+ there are symbolic links (just like POSIX realpath()).
+ */
+int t_realpath(const char *path, const char **npath_r);
+/* Like t_realpath(), but path is relative to given root. */
+int t_realpath_to(const char *path, const char *root,
+ const char **npath_r);
+
+#endif
+
More information about the dovecot-cvs
mailing list