15825705 SUNBT7206929 all of the ftp test cases with "-f" are timeouted in krb5_r_apps
17599705 proftpd NLST ../ returns ./
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/15825705.patch Fri Feb 28 11:15:50 2014 +0100
@@ -0,0 +1,414 @@
+http://bugs.proftpd.org/show_bug.cgi?id=4014
+
+I addition, this patch backports the pr_str_get_nbytes() function.
+
+diff --git a/include/options.h b/include/options.h
+index 96fa35d..0b29bf7 100644
+--- a/include/options.h
++++ b/include/options.h
+@@ -110,6 +110,13 @@
+ # define PR_TUNABLE_XFER_BUFFER_SIZE PR_TUNABLE_BUFFER_SIZE
+ #endif
+
++/* Maximum FTP command size. For details on this size of 512KB, see
++ * the Bug#4014 discussion.
++ */
++#ifndef PR_TUNABLE_CMD_BUFFER_SIZE
++# define PR_TUNABLE_CMD_BUFFER_SIZE (512 * 1024)
++#endif
++
+ /* Maximum path length. GNU HURD (and some others) do not define
+ * MAXPATHLEN. POSIX' PATH_MAX is mandated to be at least 256
+ * (according to some), so 1K, in the absense of MAXPATHLEN, should be
+diff --git a/include/str.h b/include/str.h
+index b4fed7c..1cf8724 100644
+--- a/include/str.h
++++ b/include/str.h
+@@ -39,6 +39,7 @@ char *pstrndup(pool *, const char *, size_t);
+
+ char *pr_str_strip(pool *, char *);
+ char *pr_str_strip_end(char *, char *);
++int pr_str_get_nbytes(const char *, const char *, off_t *);
+ char *pr_str_get_word(char **, int);
+
+ #define PR_STR_FL_PRESERVE_COMMENTS 0x0001
+diff --git a/modules/mod_core.c b/modules/mod_core.c
+index 18a47c2..922f4d1 100644
+--- a/modules/mod_core.c
++++ b/modules/mod_core.c
+@@ -2240,18 +2240,44 @@ MODRET set_allowforeignaddress(cmd_rec *cmd) {
+ }
+
+ MODRET set_commandbuffersize(cmd_rec *cmd) {
+- int size = 0;
++ int res;
++ size_t size = 0;
++ off_t nbytes = 0;
+ config_rec *c = NULL;
++ const char *units = NULL;
++
++ if (cmd->argc < 2 || cmd->argc > 3) {
++ CONF_ERROR(cmd, "wrong number of parameters")
++ }
+
+- CHECK_ARGS(cmd, 1);
+ CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
+
+- /* NOTE: need to add checks for maximum possible sizes, negative sizes. */
+- size = atoi(cmd->argv[1]);
++ if (cmd->argc == 3) {
++ units = cmd->argv[2];
++ }
++
++ if (pr_str_get_nbytes(cmd->argv[1], units, &nbytes) < 0) {
++ CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to parse: ",
++ cmd->argv[1], " ", units, ": ", strerror(errno), NULL));
++ }
++
++ if (nbytes > PR_TUNABLE_CMD_BUFFER_SIZE) {
++ char max[1024];
++
++ snprintf(max, sizeof(max)-1, "%lu", (unsigned long)
++ PR_TUNABLE_CMD_BUFFER_SIZE);
++ max[sizeof(max)-1] = '\0';
++
++ CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "size ", cmd->argv[1], units,
++ "exceeds max size ", max, NULL));
++ }
++
++ /* Possible truncation here, but only for an absurdly large size. */
++ size = (size_t) nbytes;
+
+ c = add_config_param(cmd->argv[0], 1, NULL);
+- c->argv[0] = pcalloc(c->pool, sizeof(int));
+- *((int *) c->argv[0]) = size;
++ c->argv[0] = pcalloc(c->pool, sizeof(size_t));
++ *((size_t *) c->argv[0]) = size;
+
+ return PR_HANDLED(cmd);
+ }
+diff --git a/src/main.c b/src/main.c
+index 3e6d637..660e14b 100644
+--- a/src/main.c
++++ b/src/main.c
+@@ -466,42 +466,21 @@ static int _dispatch(cmd_rec *cmd, int cmd_type, int validate, char *match) {
+ return success;
+ }
+
+-/* Returns the appropriate maximum buffer length to use for FTP commands
+- * from the client, taking the CommandBufferSize directive into account.
++/* Returns the appropriate maximum buffer size to use for FTP commands
++ * from the client.
+ */
+-static long get_max_cmd_len(size_t buflen) {
+- long res;
+- int *bufsz = NULL;
+- size_t default_cmd_bufsz;
+-
+- /* It's possible for the admin to select a PR_TUNABLE_BUFFER_SIZE which
+- * is smaller than PR_DEFAULT_CMD_BUFSZ. We need to handle such cases
+- * properly.
+- */
+- default_cmd_bufsz = PR_DEFAULT_CMD_BUFSZ;
+- if (default_cmd_bufsz > buflen) {
+- default_cmd_bufsz = buflen;
+- }
+-
++static size_t get_max_cmd_sz(void) {
++ size_t res;
++ size_t *bufsz = NULL;
++
+ bufsz = get_param_ptr(main_server->conf, "CommandBufferSize", FALSE);
+ if (bufsz == NULL) {
+- res = default_cmd_bufsz;
+-
+- } else if (*bufsz <= 0) {
+- pr_log_pri(PR_LOG_WARNING, "invalid CommandBufferSize size (%d) given, "
+- "using default buffer size (%lu) instead", *bufsz,
+- (unsigned long) default_cmd_bufsz);
+- res = default_cmd_bufsz;
+-
+- } else if (*bufsz + 1 > buflen) {
+- pr_log_pri(PR_LOG_WARNING, "invalid CommandBufferSize size (%d) given, "
+- "using default buffer size (%lu) instead", *bufsz,
+- (unsigned long) default_cmd_bufsz);
+- res = default_cmd_bufsz;
++ res = PR_DEFAULT_CMD_BUFSZ;
+
+ } else {
+- pr_log_debug(DEBUG1, "setting CommandBufferSize to %d", *bufsz);
+- res = (long) *bufsz;
++ pr_log_debug(DEBUG1, "setting CommandBufferSize to %lu",
++ (unsigned long) *bufsz);
++ res = *bufsz;
+ }
+
+ return res;
+@@ -509,21 +488,29 @@ static long get_max_cmd_len(size_t buflen) {
+
+ int pr_cmd_read(cmd_rec **res) {
+ static long cmd_bufsz = -1;
+- char buf[PR_DEFAULT_CMD_BUFSZ+1] = {'\0'};
++ static char *cmd_buf = NULL;
+ char *cp;
+- size_t buflen;
++ size_t cmd_buflen;
+
+ if (res == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
++ if (cmd_bufsz == -1) {
++ cmd_bufsz = get_max_cmd_sz();
++ }
++
++ if (cmd_buf == NULL) {
++ cmd_buf = pcalloc(session.pool, cmd_bufsz + 1);
++ }
++
+ while (TRUE) {
+ pr_signals_handle();
+
+- memset(buf, '\0', sizeof(buf));
++ memset(cmd_buf, '\0', cmd_bufsz);
+
+- if (pr_netio_telnet_gets(buf, sizeof(buf)-1, session.c->instrm,
++ if (pr_netio_telnet_gets(cmd_buf, cmd_bufsz, session.c->instrm,
+ session.c->outstrm) == NULL) {
+
+ if (errno == E2BIG) {
+@@ -544,9 +531,6 @@ int pr_cmd_read(cmd_rec **res) {
+ break;
+ }
+
+- if (cmd_bufsz == -1)
+- cmd_bufsz = get_max_cmd_len(sizeof(buf));
+-
+ /* This strlen(3) is guaranteed to terminate; the last byte of buf is
+ * always NUL, since pr_netio_telnet_gets() is told that the buf size is
+ * one byte less than it really is.
+@@ -554,26 +538,28 @@ int pr_cmd_read(cmd_rec **res) {
+ * If the strlen(3) says that the length is less than the cmd_bufsz, then
+ * there is no need to truncate the buffer by inserting a NUL.
+ */
+- buflen = strlen(buf);
+- if (buflen > (cmd_bufsz - 1)) {
++ cmd_buflen = strlen(cmd_buf);
++ if (cmd_buflen > cmd_bufsz) {
+ pr_log_debug(DEBUG0, "truncating incoming command length (%lu bytes) to "
+ "CommandBufferSize %lu; use the CommandBufferSize directive to increase "
+- "the allowed command length", (unsigned long) buflen,
++ "the allowed command length", (unsigned long) cmd_buflen,
+ (unsigned long) cmd_bufsz);
+- buf[cmd_bufsz - 1] = '\0';
++ cmd_buf[cmd_bufsz-1] = '\0';
+ }
+
+- if (buflen &&
+- (buf[buflen-1] == '\n' || buf[buflen-1] == '\r')) {
+- buf[buflen-1] = '\0';
+- buflen--;
++ if (cmd_buflen &&
++ (cmd_buf[cmd_buflen-1] == '\n' || cmd_buf[cmd_buflen-1] == '\r')) {
++ cmd_buf[cmd_buflen-1] = '\0';
++ cmd_buflen--;
+
+- if (buflen &&
+- (buf[buflen-1] == '\n' || buf[buflen-1] =='\r'))
+- buf[buflen-1] = '\0';
++ if (cmd_buflen &&
++ (cmd_buf[cmd_buflen-1] == '\n' || cmd_buf[cmd_buflen-1] =='\r')) {
++ cmd_buf[cmd_buflen-1] = '\0';
++ cmd_buflen--;
++ }
+ }
+
+- cp = buf;
++ cp = cmd_buf;
+ if (*cp == '\r')
+ cp++;
+
+@@ -587,11 +573,11 @@ int pr_cmd_read(cmd_rec **res) {
+ * command handlers themselves, via cmd->arg. This small hack
+ * reduces the burden on SITE module developers, however.
+ */
+- if (strncasecmp(cp, C_SITE, 4) == 0)
++ if (strncasecmp(cp, C_SITE, 4) == 0) {
+ flags |= PR_STR_FL_PRESERVE_WHITESPACE;
++ }
+
+ cmd = make_ftp_cmd(session.pool, cp, flags);
+-
+ if (cmd) {
+ *res = cmd;
+ }
+diff --git a/src/str.c b/src/str.c
+index d243a17..4f327bf 100644
+--- a/src/str.c
++++ b/src/str.c
+@@ -367,6 +367,81 @@ char *pr_str_strip_end(char *s, char *ch) {
+ return s;
+ }
+
++int pr_str_get_nbytes(const char *str, const char *units, off_t *nbytes) {
++ off_t sz;
++ char *ptr = NULL;
++ float factor = 0.0;
++
++ if (str == NULL) {
++ errno = EINVAL;
++ return -1;
++ }
++
++ /* No negative numbers. */
++ if (*str == '-') {
++ errno = EINVAL;
++ return -1;
++ }
++
++ if (units == NULL ||
++ *units == '\0') {
++ factor = 1.0;
++
++ } else if (strncasecmp(units, "KB", 3) == 0) {
++ factor = 1024.0;
++
++ } else if (strncasecmp(units, "MB", 3) == 0) {
++ factor = 1024.0 * 1024.0;
++
++ } else if (strncasecmp(units, "GB", 3) == 0) {
++ factor = 1024.0 * 1024.0 * 1024.0;
++
++ } else if (strncasecmp(units, "TB", 3) == 0) {
++ factor = 1024.0 * 1024.0 * 1024.0 * 1024.0;
++
++ } else if (strncasecmp(units, "B", 2) == 0) {
++ factor = 1.0;
++
++ } else {
++ errno = EINVAL;
++ return -1;
++ }
++
++ errno = 0;
++
++#ifdef HAVE_STRTOULL
++ sz = strtoull(str, &ptr, 10);
++#else
++ sz = strtoul(str, &ptr, 10);
++#endif /* !HAVE_STRTOULL */
++
++ if (errno == ERANGE) {
++ return -1;
++ }
++
++ if (ptr != NULL && *ptr) {
++ /* Error parsing the given string */
++ errno = EINVAL;
++ return -1;
++ }
++
++ /* Don't bother applying the factor if the result will overflow the result. */
++#ifdef ULLONG_MAX
++ if (sz > (ULLONG_MAX / factor)) {
++#else
++ if (sz > (ULONG_MAX / factor)) {
++#endif /* !ULLONG_MAX */
++ errno = ERANGE;
++ return -1;
++ }
++
++ if (nbytes != NULL) {
++ *nbytes = (off_t) (sz * factor);
++ }
++
++ return 0;
++}
++
+ char *pr_str_get_word(char **cp, int flags) {
+ char *res, *dst;
+ char quote_mode = 0;
+diff --git a/tests/t/lib/ProFTPD/Tests/Config/CommandBufferSize.pm b/tests/t/lib/ProFTPD/Tests/Config/CommandBufferSize.pm
+index ed4672a..a57c898 100644
+--- a/tests/t/lib/ProFTPD/Tests/Config/CommandBufferSize.pm
++++ b/tests/t/lib/ProFTPD/Tests/Config/CommandBufferSize.pm
+@@ -94,6 +94,8 @@ sub cmdbuffersz_small {
+ die("Can't open $test_path: $!");
+ }
+
++ my $idle_timeout = 3;
++
+ my $config = {
+ PidFile => $pid_file,
+ ScoreboardFile => $scoreboard_file,
+@@ -103,6 +105,7 @@ sub cmdbuffersz_small {
+ AuthGroupFile => $auth_group_file,
+
+ CommandBufferSize => $cmdbufsz,
++ TimeoutIdle => $idle_timeout,
+
+ IfModules => {
+ 'mod_delay.c' => {
+@@ -128,44 +131,16 @@ sub cmdbuffersz_small {
+ defined(my $pid = fork()) or die("Can't fork: $!");
+ if ($pid) {
+ eval {
+- my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
++ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+ $client->login($user, $passwd);
+
+- my $conn = $client->list_raw($test_file);
+- unless ($conn) {
+- die("Failed to LIST $test_file: " . $client->response_code() . " " .
+- $client->response_msg());
++ # Since our filename is longer than the CommandBufferSize, proftpd
++ # should simply ignore this. It will fail because of the idle timeout
++ # first.
++ eval { $client->stat($test_file) };
++ unless ($@) {
++ die("STAT command succeeded unexpectedly");
+ }
+-
+- my $buf;
+- $conn->read($buf, 8192, 30);
+- eval { $conn->close() };
+-
+- my $resp_code = $client->response_code();
+- my $resp_msg = $client->response_msg();
+-
+- # CommandBufferSize works by truncating any input longer than the
+- # configured length. (It should arguably reject such longer input,
+- # but that is a different consideration.)
+- #
+- # Since our file name is longer than the CommandBufferSize, it means
+- # the path will be truncated, and the LIST should return 450.
+-
+- my $expected;
+-
+- $expected = 450;
+- $self->assert($expected == $resp_code,
+- test_msg("Expected $expected, got $resp_code"));
+-
+- # This length is CommandBufferSize - "LIST"(4) - " "(1) - 1 for
+- # the NUL reserved in the code. Thus CommandBufferSize - 6.
+- my $truncated_name = ("A" x ($cmdbufsz - 6));
+-
+- $expected = "$truncated_name: No such file or directory";
+- $self->assert($expected eq $resp_msg,
+- test_msg("Expected '$expected', got '$resp_msg'"));
+-
+- $client->quit();
+ };
+
+ if ($@) {
+@@ -176,7 +151,7 @@ sub cmdbuffersz_small {
+ $wfh->flush();
+
+ } else {
+- eval { server_wait($config_file, $rfh) };
++ eval { server_wait($config_file, $rfh, 15) };
+ if ($@) {
+ warn($@);
+ exit 1;
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/17599705.patch Fri Feb 28 11:15:50 2014 +0100
@@ -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) {
+