# HG changeset patch # User Tomas Klacko # Date 1393582550 -3600 # Node ID 4192dd72f2736c0232346b9483598df4e5852310 # Parent 01b1a705eae103b2c3ee0cc55f5fd85b59a7dfd7 15825705 SUNBT7206929 all of the ftp test cases with "-f" are timeouted in krb5_r_apps 17599705 proftpd NLST ../ returns ./ diff -r 01b1a705eae1 -r 4192dd72f273 components/proftpd/patches/15825705.patch --- /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; + diff -r 01b1a705eae1 -r 4192dd72f273 components/proftpd/patches/17599705.patch --- /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) { +