--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/17599705.patch Tue Feb 25 02:29:07 2014 -0800
@@ -0,0 +1,315 @@
+http://bugs.proftpd.org/show_bug.cgi?id=4011
+
+diff --git a/include/fsio.h b/include/fsio.h
+index 07c18c1..a4c395e 100644
+--- a/include/fsio.h
++++ b/include/fsio.h
+@@ -189,6 +189,9 @@ struct fh_rec {
+ size_t fh_iosz;
+ };
+
++/* Maximum symlink count, for loop detection. */
++#define PR_FSIO_MAX_LINK_COUNT 32
++
+ /* Macros for that code that needs to get into the internals of pr_fs_t.
+ * (These will help keep the internals as opaque as possible).
+ */
+@@ -319,7 +322,11 @@ char *pr_fs_encode_path(pool *, const char *);
+ int pr_fs_use_encoding(int bool);
+ int pr_fs_valid_path(const char *);
+ void pr_fs_virtual_path(const char *, char *, size_t);
++
+ void pr_fs_clean_path(const char *, char *, size_t);
++int pr_fs_clean_path2(const char *, char *, size_t, int);
++#define PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH 0x001
++
+ int pr_fs_glob(const char *, int, int (*errfunc)(const char *, int), glob_t *);
+ void pr_fs_globfree(glob_t *);
+ void pr_resolve_fs_map(void);
+diff --git a/modules/mod_ls.c b/modules/mod_ls.c
+index 50e8035..58e81d7 100644
+--- a/modules/mod_ls.c
++++ b/modules/mod_ls.c
+@@ -2571,46 +2571,6 @@ MODRET ls_nlst(cmd_rec *cmd) {
+ }
+ }
+
+- /* Clean the path. */
+- if (*target != '/') {
+- size_t cwdlen = strlen(pr_fs_getcwd());
+-
+- pr_fs_clean_path(pdircat(cmd->tmp_pool, pr_fs_getcwd(), target, NULL),
+- buf, sizeof(buf));
+-
+- target = buf;
+-
+- /* If the given target was not an absolute path, advance past the
+- * current working directory prefix in the cleaned up target path.
+- */
+- target += cwdlen;
+-
+- /* If the length of the current working directory (cwdlen) is one,
+- * it means that the current working directory is the root ('/'),
+- * and so we don't want to advance past that into the file name
+- * portion of the path.
+- */
+- if (cwdlen > 1)
+- target += 1;
+-
+- } else {
+- pr_fs_clean_path(target, buf, sizeof(buf));
+- target = buf;
+- }
+-
+- /* Remove any trailing separators. */
+- targetlen = strlen(target);
+- while (targetlen >= 1 &&
+- target[targetlen-1] == '/') {
+-
+- if (strcmp(target, "/") == 0) {
+- break;
+- }
+-
+- target[targetlen-1] = '\0';
+- targetlen = strlen(target);
+- }
+-
+ /* If the target is a glob, get the listing of files/dirs to send. */
+ if (use_globbing &&
+ pr_str_is_fnmatch(target)) {
+@@ -2715,12 +2675,36 @@ MODRET ls_nlst(cmd_rec *cmd) {
+ }
+
+ } else {
+-
+ /* A single target. If it's a directory, list the contents; if it's a
+ * file, just list the file.
+ */
+ struct stat st;
+
++ if (!is_dotdir(target)) {
++ /* Clean the path. */
++ if (*target != '/') {
++ pr_fs_clean_path2(target, buf, sizeof(buf), 0);
++
++ } else {
++ pr_fs_clean_path(target, buf, sizeof(buf));
++ }
++
++ target = buf;
++
++ } else {
++ /* Remove any trailing separators. */
++ targetlen = strlen(target);
++ while (targetlen >= 1 &&
++ target[targetlen-1] == '/') {
++ if (strncmp(target, "/", 2) == 0) {
++ break;
++ }
++
++ target[targetlen-1] = '\0';
++ targetlen = strlen(target);
++ }
++ }
++
+ if (!ls_perms_full(cmd->tmp_pool, cmd, target, &hidden)) {
+ int xerrno = errno;
+
+diff --git a/src/fsio.c b/src/fsio.c
+index 782168d..4d191fe 100644
+--- a/src/fsio.c
++++ b/src/fsio.c
+@@ -1627,8 +1627,8 @@ int pr_fs_resolve_partial(const char *path, char *buf, size_t buflen, int op) {
+
+ pr_fs_t *fs = NULL;
+ int len = 0, fini = 1, link_cnt = 0;
+- ino_t last_inode = 0;
+- dev_t last_device = 0;
++ ino_t prev_inode = 0;
++ dev_t prev_device = 0;
+ struct stat sbuf;
+
+ if (!path) {
+@@ -1740,16 +1740,16 @@ int pr_fs_resolve_partial(const char *path, char *buf, size_t buflen, int op) {
+ char linkpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
+
+ /* Detect an obvious recursive symlink */
+- if (sbuf.st_ino && (ino_t) sbuf.st_ino == last_inode &&
+- sbuf.st_dev && (dev_t) sbuf.st_dev == last_device) {
++ if (sbuf.st_ino && (ino_t) sbuf.st_ino == prev_inode &&
++ sbuf.st_dev && (dev_t) sbuf.st_dev == prev_device) {
+ errno = ELOOP;
+ return -1;
+ }
+
+- last_inode = (ino_t) sbuf.st_ino;
+- last_device = (dev_t) sbuf.st_dev;
++ prev_inode = (ino_t) sbuf.st_ino;
++ prev_device = (dev_t) sbuf.st_dev;
+
+- if (++link_cnt > 32) {
++ if (++link_cnt > PR_FSIO_MAX_LINK_COUNT) {
+ errno = ELOOP;
+ return -1;
+ }
+@@ -1820,8 +1820,8 @@ int pr_fs_resolve_path(const char *path, char *buf, size_t buflen, int op) {
+ pr_fs_t *fs = NULL;
+
+ int len = 0, fini = 1, link_cnt = 0;
+- ino_t last_inode = 0;
+- dev_t last_device = 0;
++ ino_t prev_inode = 0;
++ dev_t prev_device = 0;
+ struct stat sbuf;
+
+ if (!path) {
+@@ -1906,16 +1906,16 @@ int pr_fs_resolve_path(const char *path, char *buf, size_t buflen, int op) {
+ char linkpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
+
+ /* Detect an obvious recursive symlink */
+- if (sbuf.st_ino && (ino_t) sbuf.st_ino == last_inode &&
+- sbuf.st_dev && (dev_t) sbuf.st_dev == last_device) {
++ if (sbuf.st_ino && (ino_t) sbuf.st_ino == prev_inode &&
++ sbuf.st_dev && (dev_t) sbuf.st_dev == prev_device) {
+ errno = ELOOP;
+ return -1;
+ }
+
+- last_inode = (ino_t) sbuf.st_ino;
+- last_device = (dev_t) sbuf.st_dev;
++ prev_inode = (ino_t) sbuf.st_ino;
++ prev_device = (dev_t) sbuf.st_dev;
+
+- if (++link_cnt > 32) {
++ if (++link_cnt > PR_FSIO_MAX_LINK_COUNT) {
+ errno = ELOOP;
+ return -1;
+ }
+@@ -1977,22 +1977,33 @@ int pr_fs_resolve_path(const char *path, char *buf, size_t buflen, int op) {
+ return 0;
+ }
+
+-void pr_fs_clean_path(const char *path, char *buf, size_t buflen) {
++int pr_fs_clean_path2(const char *path, char *buf, size_t buflen, int flags) {
+ char workpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
+ char curpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
+ char namebuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
+- char *where = NULL, *ptr = NULL, *last = NULL;
+- int fini = 1;
++ int fini = 1, have_abs_path = FALSE;
+
+- if (!path)
+- return;
++ if (path == NULL ||
++ buf == NULL) {
++ errno = EINVAL;
++ return -1;
++ }
++
++ if (buflen == 0) {
++ return 0;
++ }
+
+ sstrncpy(curpath, path, sizeof(curpath));
+
++ if (*curpath == '/') {
++ have_abs_path = TRUE;
++ }
++
+ /* main loop */
+ while (fini--) {
+- where = curpath;
++ char *where = NULL, *ptr = NULL, *last = NULL;
+
++ where = curpath;
+ while (*where != '\0') {
+ pr_signals_handle();
+
+@@ -2013,8 +2024,12 @@ void pr_fs_clean_path(const char *path, char *buf, size_t buflen) {
+ ptr = last = workpath;
+
+ while (*ptr) {
+- if (*ptr == '/')
++ pr_signals_handle();
++
++ if (*ptr == '/') {
+ last = ptr;
++ }
++
+ ptr++;
+ }
+
+@@ -2028,34 +2043,46 @@ void pr_fs_clean_path(const char *path, char *buf, size_t buflen) {
+ ptr = last = workpath;
+
+ while (*ptr) {
+- if (*ptr == '/')
++ pr_signals_handle();
++
++ if (*ptr == '/') {
+ last = ptr;
++ }
+ ptr++;
+ }
++
+ *last = '\0';
+ continue;
+ }
+- ptr = strchr(where, '/');
+
+- if (!ptr) {
++ ptr = strchr(where, '/');
++ if (ptr == NULL) {
+ size_t wherelen = strlen(where);
+
+ ptr = where;
+- if (wherelen >= 1)
++ if (wherelen >= 1) {
+ ptr += (wherelen - 1);
++ }
+
+- } else
++ } else {
+ *ptr = '\0';
++ }
+
+ sstrncpy(namebuf, workpath, sizeof(namebuf));
+
+ if (*namebuf) {
+ for (last = namebuf; *last; last++);
+- if (*--last != '/')
++ if (*--last != '/') {
+ sstrcat(namebuf, "/", sizeof(namebuf)-1);
++ }
+
+- } else
+- sstrcat(namebuf, "/", sizeof(namebuf)-1);
++ } else {
++ if (have_abs_path ||
++ (flags & PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH)) {
++ sstrcat(namebuf, "/", sizeof(namebuf)-1);
++ have_abs_path = FALSE;
++ }
++ }
+
+ sstrcat(namebuf, where, sizeof(namebuf)-1);
+ namebuf[sizeof(namebuf)-1] = '\0';
+@@ -2066,10 +2093,16 @@ void pr_fs_clean_path(const char *path, char *buf, size_t buflen) {
+ }
+ }
+
+- if (!workpath[0])
++ if (!workpath[0]) {
+ sstrncpy(workpath, "/", sizeof(workpath));
++ }
+
+ sstrncpy(buf, workpath, buflen);
++ return 0;
++}
++
++void pr_fs_clean_path(const char *path, char *buf, size_t buflen) {
++ pr_fs_clean_path2(path, buf, buflen, PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH);
+ }
+
+ int pr_fs_use_encoding(int bool) {
+