[Git][debian-proftpd-team/proftpd-mod-vroot][master] 4 commits: New upstream version 0.9.10

Hilmar Preuße (@hilmar-guest) gitlab at salsa.debian.org
Wed Aug 3 21:55:45 BST 2022



Hilmar Preuße pushed to branch master at Debian ProFTPD Team / proftpd-mod-vroot


Commits:
094ea010 by Hilmar Preusse at 2022-08-03T22:20:32+02:00
New upstream version 0.9.10
- - - - -
86d767cf by Hilmar Preusse at 2022-08-03T22:20:34+02:00
Update upstream source from tag 'upstream/0.9.10'

Update to upstream version '0.9.10'
with Debian dir 30deef676933091e0343b9954bdc32585f448a0e
- - - - -
ef2318a3 by Hilmar Preusse at 2022-08-03T22:53:32+02:00
"git push" should update pristine-tar too.

- - - - -
9bdb030e by Hilmar Preusse at 2022-08-03T22:54:31+02:00
Update debian/changelog.

- - - - -


12 changed files:

- .github/workflows/ci.yml
- .gitignore
- debian/changelog
- debian/gbp.conf
- mod_vroot.h.in
- path.c
- t/Makefile.in
- t/api/alias.c
- t/api/fsio.c
- t/api/path.c
- t/lib/ProFTPD/Tests/Modules/mod_vroot.pm
- t/lib/ProFTPD/Tests/Modules/mod_vroot/sftp.pm


Changes:

=====================================
.github/workflows/ci.yml
=====================================
@@ -7,6 +7,8 @@ on:
   pull_request:
     branches:
       - master
+  schedule:
+    - cron: '11 1 * * 0'
 
 jobs:
   build:
@@ -18,8 +20,8 @@ jobs:
           - clang
           - gcc
         container:
+          - almalinux:8
           - alpine:3.14
-          - centos:8
           - ubuntu:18.04
 
     container: ${{ matrix.container }}
@@ -66,17 +68,18 @@ jobs:
           gcc --version
           openssl version -a
 
-      - name: Install Centos packages
-        if: ${{ matrix.container == 'centos:8' }}
+      - name: Install RPM packages
+        if: ${{ matrix.container == 'almalinux:8' }}
         run: |
           # Need to add other repos for e.g. libsodium
-          yum install -y dnf-plugins-core epel-release clang gcc make zlib-devel
-          yum config-manager --set-enabled powertools
+          yum install -y dnf-plugins-core epel-release yum-utils clang gcc make zlib-devel
+          dnf config-manager --enable epel
+          dnf config-manager --set-enabled powertools
           # for unit tests
           yum install -y check-devel https://cbs.centos.org/kojifiles/packages/subunit/1.4.0/1.el8/x86_64/subunit-1.4.0-1.el8.x86_64.rpm https://cbs.centos.org/kojifiles/packages/subunit/1.4.0/1.el8/x86_64/subunit-devel-1.4.0-1.el8.x86_64.rpm
 
           # for OpenSSL support
-          yum install -y openssl-devel
+          yum install -y openssl openssl-devel
 
           # for debugging
           clang --version
@@ -103,6 +106,7 @@ jobs:
             libfile-copy-recursive-perl \
             libfile-path-tiny-perl \
             libfile-spec-native-perl \
+            libnet-inet6glue-perl \
             libnet-ssh2-perl \
             libnet-ssleay-perl \
             libnet-telnet-perl \


=====================================
.gitignore
=====================================
@@ -3,6 +3,7 @@ Makefile
 config.status
 autom4te.cache
 mod_vroot.h
+t/api-tests
 .libs
 .*.swp
 *.la


=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+proftpd-mod-vroot (0.9.10-1) UNRELEASED; urgency=medium
+
+  * New upstream release.
+
+ -- Hilmar Preusse <hille42 at web.de>  Wed, 03 Aug 2022 22:21:16 +0200
+
 proftpd-mod-vroot (0.9.9-1) unstable; urgency=medium
 
   [ Hilmar Preusse ]


=====================================
debian/gbp.conf
=====================================
@@ -1,2 +1,5 @@
 [import-orig]
 pristine-tar = True
+
+[push]
+pristine-tar = True


=====================================
mod_vroot.h.in
=====================================
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_vroot
- * Copyright (c) 2016-2019 TJ Saunders
+ * Copyright (c) 2016-2022 TJ Saunders
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -27,7 +27,7 @@
 
 #include "conf.h"
 
-#define MOD_VROOT_VERSION			"mod_vroot/0.9.9"
+#define MOD_VROOT_VERSION			"mod_vroot/0.9.10"
 
 /* Make sure the version of proftpd is as necessary. */
 #if PROFTPD_VERSION_NUMBER < 0x0001030602


=====================================
path.c
=====================================
@@ -1,6 +1,6 @@
 /*
  * ProFTPD: mod_vroot Path API
- * Copyright (c) 2016 TJ Saunders
+ * Copyright (c) 2016-2022 TJ Saunders
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -69,18 +69,25 @@ const char *vroot_path_get_base(pool *p, size_t *baselen) {
 }
 
 int vroot_path_set_base(const char *base, size_t baselen) {
-  if (base == NULL) {
+  if (base == NULL ||
+      baselen >= sizeof(vroot_base)) {
     errno = EINVAL;
     return -1;
   }
 
   memset(vroot_base, '\0', sizeof(vroot_base));
-  memcpy(vroot_base, base, sizeof(vroot_base)-1);
+  if (baselen > 0) {
+    memcpy(vroot_base, base, baselen);
+    vroot_base[sizeof(vroot_base)-1] = '\0';
+  }
   vroot_baselen = baselen;
 
   return 0;
 }
 
+/* Note that we do in-place modifications of the given `path` buffer here,
+ * which means that it MUST be writable; no constant strings, please.
+ */
 void vroot_path_clean(char *path) {
   char *ptr = NULL;
 
@@ -91,17 +98,23 @@ void vroot_path_clean(char *path) {
 
   ptr = strstr(path, "//");
   while (ptr != NULL) {
+    pr_signals_handle();
+
     strmove(ptr, ptr + 1);
     ptr = strstr(path, "//");
   }
 
   ptr = strstr(path, "/./");
   while (ptr != NULL) {
+    pr_signals_handle();
+
     strmove(ptr, ptr + 2);
     ptr = strstr(path, "/./");
   }
 
   while (strncmp(path, "../", 3) == 0) {
+    pr_signals_handle();
+
     path += 3;
   }
 
@@ -109,6 +122,8 @@ void vroot_path_clean(char *path) {
   if (ptr != NULL) {
     if (ptr == path) {
       while (strncmp(path, "/../", 4) == 0) {
+        pr_signals_handle();
+
         strmove(path, path + 3);
       }
 
@@ -116,7 +131,11 @@ void vroot_path_clean(char *path) {
     }
 
     while (ptr != NULL) {
-      char *next_elem = ptr + 4;
+      char *next_elem;
+
+      pr_signals_handle();
+
+      next_elem = ptr + 4;
 
       if (ptr != path &&
           *ptr == '/') {
@@ -197,14 +216,16 @@ char *vroot_realpath(pool *p, const char *path, int flags) {
   char *real_path = NULL;
   size_t real_pathlen;
 
-  if (flags & VROOT_REALPATH_FL_ABS_PATH) {
-    /* If not an absolute path, prepend the current location. */
-    if (*path != '/') {
-      real_path = pdircat(p, pr_fs_getvwd(), path, NULL);
+  if (p == NULL ||
+      path == NULL) {
+    errno = EINVAL;
+    return NULL;
+  }
 
-    } else {
-      real_path = pstrdup(p, path);
-    }
+  /* If not an absolute path, prepend the current location. */
+  if (*path != '/' &&
+      (flags & VROOT_REALPATH_FL_ABS_PATH)) {
+    real_path = pdircat(p, pr_fs_getvwd(), path, NULL);
 
   } else {
     real_path = pstrdup(p, path);
@@ -225,18 +246,31 @@ char *vroot_realpath(pool *p, const char *path, int flags) {
   return real_path;
 }
 
-int vroot_path_lookup(pool *p, char *path, size_t pathlen, const char *dir,
+/* The given `vpath` buffer is the looked-up path for the given `path`. */
+int vroot_path_lookup(pool *p, char *vpath, size_t vpathsz, const char *path,
     int flags, char **alias_path) {
   char buf[PR_TUNABLE_PATH_MAX + 1], *bufp = NULL;
+  const char *cwd;
+
+  if (vpath == NULL ||
+      path == NULL) {
+    errno = EINVAL;
+    return -1;
+  }
 
   memset(buf, '\0', sizeof(buf));
-  memset(path, '\0', pathlen);
+  if (vpath != NULL &&
+      vpathsz > 0) {
+    memset(vpath, '\0', vpathsz);
+  }
+
+  cwd = pr_fs_getcwd();
 
-  if (strcmp(dir, ".") != 0) {
-    sstrncpy(buf, dir, sizeof(buf));
+  if (strcmp(path, ".") != 0) {
+    sstrncpy(buf, path, sizeof(buf));
 
   } else {
-    sstrncpy(buf, pr_fs_getcwd(), sizeof(buf));
+    sstrncpy(buf, cwd, sizeof(buf));
   }
 
   vroot_path_clean(buf);
@@ -244,7 +278,17 @@ int vroot_path_lookup(pool *p, char *path, size_t pathlen, const char *dir,
   bufp = buf;
 
   if (strncmp(bufp, vroot_base, vroot_baselen) == 0) {
-    bufp += vroot_baselen;
+    size_t len;
+
+    /* Attempt to handle cases like "/base/base" and "/base/basefoo", where
+     * the base is just "/base".
+     * See https://github.com/proftpd/proftpd/issues/1491
+     */
+    len = strlen(bufp);
+    if (len > vroot_baselen &&
+        bufp[vroot_baselen] == '/') {
+      bufp += vroot_baselen;
+    }
   }
 
 loop:
@@ -254,19 +298,19 @@ loop:
       bufp[1] == '.' &&
       (bufp[2] == '\0' ||
        bufp[2] == '/')) {
-    char *tmp = NULL;
+    char *ptr = NULL;
 
-    tmp = strrchr(path, '/');
-    if (tmp != NULL) {
-      *tmp = '\0';
+    ptr = strrchr(vpath, '/');
+    if (ptr != NULL) {
+      *ptr = '\0';
 
     } else {
-      *path = '\0';
+      *vpath = '\0';
     }
 
-    if (strncmp(path, vroot_base, vroot_baselen) == 0 ||
-         path[vroot_baselen] != '/') {
-      snprintf(path, pathlen, "%s/", vroot_base);
+    if (strncmp(vpath, vroot_base, vroot_baselen) == 0 ||
+         vpath[vroot_baselen] != '/') {
+      snprintf(vpath, vpathsz, "%s/", vroot_base);
     }
 
     if (bufp[0] == '.' &&
@@ -277,7 +321,7 @@ loop:
     }
 
   } else if (*bufp == '/') {
-    snprintf(path, pathlen, "%s/", vroot_base);
+    snprintf(vpath, vpathsz, "%s/", vroot_base);
     bufp += 1;
     goto loop;
 
@@ -319,19 +363,19 @@ loop:
     }
 
     buflen = strlen(bufp) + 1;
-    tmplen = strlen(path);
+    tmplen = strlen(vpath);
 
-    if (tmplen + buflen >= pathlen) {
+    if (tmplen + buflen >= vpathsz) {
       errno = ENAMETOOLONG;
       return -1;
     }
 
-    path[tmplen] = '/';
-    memcpy(path + tmplen + 1, bufp, buflen);
+    vpath[tmplen] = '/';
+    memcpy(vpath + tmplen + 1, bufp, buflen);
   }
 
   /* Clean any unnecessary characters added by the above processing. */
-  vroot_path_clean(path);
+  vroot_path_clean(vpath);
 
   if (!(flags & VROOT_LOOKUP_FL_NO_ALIAS)) {
     int alias_count;
@@ -346,7 +390,7 @@ loop:
        * aliases are found.
        */
       bufp = buf;
-      start_ptr = path;
+      start_ptr = vpath;
 
       while (start_ptr != NULL) {
         char *ptr = NULL;
@@ -376,11 +420,11 @@ loop:
               *alias_path, start_ptr);
           }
 
-          sstrncpy(path, src_path, pathlen);
+          sstrncpy(vpath, src_path, vpathsz);
 
           if (end_ptr != NULL) {
             /* Now tack on our suffix from the scratchpad. */
-            sstrcat(path, bufp, pathlen);
+            sstrcat(vpath, bufp, vpathsz);
           }
 
           break;
@@ -409,5 +453,11 @@ loop:
     }
   }
 
+  /* Note that logging the session.chroot_path here will not help; mod_vroot
+   * deliberately always sets that to just "/".
+   */
+  pr_trace_msg(trace_channel, 19,
+    "lookup: path = '%s', cwd = '%s', base = '%s', vpath = '%s'", path, cwd,
+    vroot_base, vpath);
   return 0;
 }


=====================================
t/Makefile.in
=====================================
@@ -23,6 +23,8 @@ TEST_API_DEPS=\
   $(top_srcdir)/src/str.o \
   $(top_srcdir)/src/sets.o \
   $(top_srcdir)/src/table.o \
+  $(top_srcdir)/src/netacl.o \
+  $(top_srcdir)/src/class.o \
   $(top_srcdir)/src/event.o \
   $(top_srcdir)/src/timers.o \
   $(top_srcdir)/src/stash.o \


=====================================
t/api/alias.c
=====================================
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_vroot testsuite
- * Copyright (c) 2016 TJ Saunders <tj at castaglia.org>
+ * Copyright (c) 2016-2022 TJ Saunders <tj at castaglia.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -58,8 +58,8 @@ START_TEST (alias_init_test) {
   int res;
 
   res = vroot_alias_init(NULL);
-  fail_unless(res < 0, "Failed to handle null pool");
-  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
+  ck_assert_msg(res < 0, "Failed to handle null pool");
+  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
     strerror(errno), errno);
 }
 END_TEST
@@ -68,7 +68,7 @@ START_TEST (alias_count_test) {
   unsigned int res;
 
   res = vroot_alias_count();
-  fail_unless(res == 0, "Expected 0, got %u", res);
+  ck_assert_msg(res == 0, "Expected 0, got %u", res);
 }
 END_TEST
 
@@ -77,11 +77,11 @@ START_TEST (alias_exists_test) {
   const char *path;
 
   res = vroot_alias_exists(NULL);
-  fail_unless(res == FALSE, "Failed to handle null path");
+  ck_assert_msg(res == FALSE, "Failed to handle null path");
 
   path = "/foo/bar";
   res = vroot_alias_exists(path);
-  fail_unless(res == FALSE, "Expected FALSE for path '%s', got TRUE", path);
+  ck_assert_msg(res == FALSE, "Expected FALSE for path '%s', got TRUE", path);
 }
 END_TEST
 
@@ -90,19 +90,19 @@ START_TEST (alias_add_test) {
   const char *dst, *src;
 
   res = vroot_alias_add(NULL, NULL);
-  fail_unless(res < 0, "Failed to handle null dst");
-  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
+  ck_assert_msg(res < 0, "Failed to handle null dst");
+  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
     strerror(errno), errno);
 
   dst = "foo";
   res = vroot_alias_add(dst, NULL);
-  fail_unless(res < 0, "Failed to handle null src");
-  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
+  ck_assert_msg(res < 0, "Failed to handle null src");
+  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
     strerror(errno), errno);
 
   src = "bar";
   res = vroot_alias_add(dst, src);
-  fail_unless(res == 0, "Failed to add alias '%s => %s': %s", src, dst,
+  ck_assert_msg(res == 0, "Failed to add alias '%s => %s': %s", src, dst,
     strerror(errno));
 }
 END_TEST
@@ -111,15 +111,15 @@ START_TEST (alias_get_test) {
   const char *alias, *path;
 
   alias = vroot_alias_get(NULL);
-  fail_unless(alias == NULL, "Failed to handle null path");
-  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
+  ck_assert_msg(alias == NULL, "Failed to handle null path");
+  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
     strerror(errno), errno);
 
   path = "/foo/bar";
   alias = vroot_alias_get(path);
-  fail_unless(alias == NULL, "Expected null for path '%s', got '%s'", path,
+  ck_assert_msg(alias == NULL, "Expected null for path '%s', got '%s'", path,
     alias);
-  fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
+  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
     strerror(errno), errno);
 }
 END_TEST
@@ -128,8 +128,8 @@ START_TEST (alias_do_test) {
   int res;
 
   res = vroot_alias_do(NULL, NULL);
-  fail_unless(res < 0, "Failed to handle null callback");
-  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
+  ck_assert_msg(res < 0, "Failed to handle null callback");
+  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
     strerror(errno), errno);
 }
 END_TEST


=====================================
t/api/fsio.c
=====================================
@@ -50,6 +50,9 @@ static void tear_down(void) {
   } 
 }
 
+/* TODO: Fill in these FSIO API tests, once the Path API unit tests are
+ * fleshed out more completely, as the FSIO API heavily relies on the Path API.
+ */
 START_TEST (fsio_stat_test) {
 }
 END_TEST


=====================================
t/api/path.c
=====================================
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_vroot testsuite
- * Copyright (c) 2016 TJ Saunders <tj at castaglia.org>
+ * Copyright (c) 2016-2022 TJ Saunders <tj at castaglia.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -40,25 +40,352 @@ static void set_up(void) {
 }
 
 static void tear_down(void) {
+  (void) vroot_path_set_base("", 0);
+
   if (getenv("TEST_VERBOSE") != NULL) {
     pr_trace_set_levels("vroot.path", 0, 0);
   }
 
-  if (p) {
+  if (p != NULL) {
     destroy_pool(p);
     p = NULL;
   } 
 }
 
+START_TEST (path_have_base_test) {
+  int res;
+
+  mark_point();
+  res = vroot_path_have_base();
+  ck_assert_msg(res == FALSE, "Have vroot base unexpectedly");
+}
+END_TEST
+
+START_TEST (path_get_base_test) {
+  const char *res;
+
+  mark_point();
+  res = vroot_path_get_base(NULL, NULL);
+  ck_assert_msg(res == NULL, "Failed to handle null pool");
+  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
+    strerror(errno), errno);
+
+  mark_point();
+  res = vroot_path_get_base(p, NULL);
+  ck_assert_msg(res != NULL, "Failed to get base: %s", strerror(errno));
+  ck_assert_msg(strcmp(res, "") == 0, "Expected '', got '%s'", res);
+}
+END_TEST
+
+START_TEST (path_set_base_test) {
+  int res;
+  const char *path, *ptr;
+  size_t pathlen, len;
+
+  mark_point();
+  res = vroot_path_set_base(NULL, 0);
+  ck_assert_msg(res < 0, "Failed to handle missing path");
+  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
+    strerror(errno), errno);
+
+  mark_point();
+  path = "/foo";
+  pathlen = (PR_TUNABLE_PATH_MAX * 4);
+  res = vroot_path_set_base("foo", pathlen);
+  ck_assert_msg(res < 0, "Failed to handle too-long pathlen");
+  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
+    strerror(errno), errno);
+
+  mark_point();
+  path = "/foo";
+  pathlen = strlen(path);
+  res = vroot_path_set_base(path, pathlen);
+  ck_assert_msg(res == 0, "Failed to set base '%s': %s", path, strerror(errno));
+
+  mark_point();
+  res = vroot_path_have_base();
+  ck_assert_msg(res == TRUE, "Have base is unexpectedly false");
+
+  mark_point();
+  ptr = vroot_path_get_base(p, &len);
+  ck_assert_msg(ptr != NULL, "Failed to get base: %s", strerror(errno));
+  ck_assert_msg(len == pathlen, "Expected %lu, got %lu",
+    (unsigned long) pathlen, (unsigned long) len);
+
+  /* Clear the base, using an empty string. */
+  mark_point();
+  path = "";
+  res = vroot_path_set_base(path, 0);
+  ck_assert_msg(res == 0, "Failed to set empty path as base: %s",
+    strerror(errno));
+
+  mark_point();
+  res = vroot_path_have_base();
+  ck_assert_msg(res == FALSE, "Have base is unexpectedly true");
+}
+END_TEST
+
 START_TEST (path_clean_test) {
+  char *path, *expected;
+
+  mark_point();
+  vroot_path_clean(NULL);
+
+  mark_point();
+  path = pstrdup(p, "//");
+  expected = "/";
+  vroot_path_clean(path);
+  ck_assert_msg(strcmp(path, expected) == 0, "Expected '%s', got '%s'",
+    expected, path);
+
+  mark_point();
+  path = pstrdup(p, "/foo/./bar//");
+  expected = "/foo/bar/";
+  vroot_path_clean(path);
+  ck_assert_msg(strcmp(path, expected) == 0, "Expected '%s', got '%s'",
+    expected, path);
+
+  mark_point();
+  path = pstrdup(p, "/foo/../bar//");
+  expected = "/bar/";
+  vroot_path_clean(path);
+  ck_assert_msg(strcmp(path, expected) == 0, "Expected '%s', got '%s'",
+    expected, path);
+
+  mark_point();
+  path = pstrdup(p, "/./.././.././bar/./");
+  expected = "/bar/";
+  vroot_path_clean(path);
+  ck_assert_msg(strcmp(path, expected) == 0, "Expected '%s', got '%s'",
+    expected, path);
+
+  mark_point();
+  path = pstrdup(p, ".");
+  expected = ".";
+  vroot_path_clean(path);
+  ck_assert_msg(strcmp(path, expected) == 0, "Expected '%s', got '%s'",
+    expected, path);
 }
 END_TEST
 
 START_TEST (realpath_test) {
+  char *res, *path, *expected;
+
+  mark_point();
+  res = vroot_realpath(NULL, NULL, 0);
+  ck_assert_msg(res == NULL, "Failed to handle null pool");
+  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
+    strerror(errno), errno);
+
+  mark_point();
+  res = vroot_realpath(p, NULL, 0);
+  ck_assert_msg(res == NULL, "Failed to handle null path");
+  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
+    strerror(errno), errno);
+
+  mark_point();
+  path = pstrdup(p, "/foo");
+  expected = "/foo";
+  res = vroot_realpath(p, path, 0);
+  ck_assert_msg(res != NULL, "Failed to handle path: '%s'", strerror(errno));
+  ck_assert_msg(strcmp(res, expected) == 0, "Expected '%s', got '%s'",
+    expected, res);
+
+  mark_point();
+  path = pstrdup(p, "/foo/");
+  expected = "/foo";
+  res = vroot_realpath(p, path, 0);
+  ck_assert_msg(res != NULL, "Failed to handle path: '%s'", strerror(errno));
+  ck_assert_msg(strcmp(res, expected) == 0, "Expected '%s', got '%s'",
+    expected, res);
+
+  mark_point();
+  path = pstrdup(p, "/foo//");
+  expected = "/foo";
+  res = vroot_realpath(p, path, 0);
+  ck_assert_msg(res != NULL, "Failed to handle path: '%s'", strerror(errno));
+  ck_assert_msg(strcmp(res, expected) == 0, "Expected '%s', got '%s'",
+    expected, res);
 }
 END_TEST
 
 START_TEST (path_lookup_test) {
+  int res;
+  char *vpath = NULL;
+  size_t vpathsz = 1024;
+  const char *path;
+
+  mark_point();
+  vpath = pcalloc(p, vpathsz);
+  res = vroot_path_lookup(p, vpath, vpathsz, NULL, 0, NULL);
+  ck_assert_msg(res < 0, "Failed to handle null path");
+  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
+    strerror(errno), errno);
+
+  mark_point();
+  path = "/";
+  res = vroot_path_lookup(NULL, NULL, 0, path, 0, NULL);
+  ck_assert_msg(res < 0, "Failed to handle null vpath");
+  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
+    strerror(errno), errno);
+
+  mark_point();
+  res = vroot_path_lookup(p, vpath, vpathsz, path, 0, NULL);
+  ck_assert_msg(res >= 0, "Failed to lookup vpath for '%s': %s", path,
+    strerror(errno));
+
+  mark_point();
+  path = ".";
+  res = vroot_path_lookup(p, vpath, vpathsz, path, 0, NULL);
+  ck_assert_msg(res >= 0, "Failed to lookup vpath for '%s': %s", path,
+    strerror(errno));
+}
+END_TEST
+
+/* See: https://github.com/proftpd/proftpd/issues/1491 */
+START_TEST (path_lookup_issue1491_test) {
+  int res;
+  char *vpath = NULL;
+  size_t vpathsz = 1024, baselen;
+  const char *base, *path, *expected;
+
+  vpath = pcalloc(p, vpathsz);
+  base = "/store";
+  baselen = strlen(base);
+
+  /* Set the base. */
+  mark_point();
+  res = vroot_path_set_base(base, baselen);
+  ck_assert_msg(res == 0, "Failed to set base '%s': %s", base, strerror(errno));
+
+  /* Start with an absolute path that matches the base. */
+  mark_point();
+  path = base;
+
+  /* NOTE: Yes, this is a surprising expectation; it has to do with the
+   * necessary fixes for Issue #1491.  Sigh.
+   */
+  expected = "/store/store";
+  res = vroot_path_lookup(p, vpath, vpathsz, path, 0, NULL);
+  ck_assert_msg(res >= 0, "Failed to lookup vpath for '%s': %s", path,
+    strerror(errno));
+  ck_assert_msg(strcmp(vpath, expected) == 0, "Expected '%s', got '%s'",
+    expected, vpath);
+
+  /* Then try a relative path whose name matches the base, sans the leading
+   * path delimiter.
+   */
+  mark_point();
+  path = "store";
+  expected = base;
+  res = vroot_path_lookup(p, vpath, vpathsz, path, 0, NULL);
+  ck_assert_msg(res >= 0, "Failed to lookup vpath for '%s': %s", path,
+    strerror(errno));
+  ck_assert_msg(strcmp(vpath, expected) == 0, "Expected '%s', got '%s'",
+    expected, vpath);
+
+  /* Next, try a relative path for a file whose name starts with that of
+   * the base.
+   */
+  mark_point();
+  path = "storetest";
+  expected = "/storetest";
+  res = vroot_path_lookup(p, vpath, vpathsz, path, 0, NULL);
+  ck_assert_msg(res >= 0, "Failed to lookup vpath for '%s': %s", path,
+    strerror(errno));
+  ck_assert_msg(strcmp(vpath, expected) == 0, "Expected '%s', got '%s'",
+    expected, vpath);
+
+  /* Next, use an absolute path for a file whose name starts with that of the
+   * base; this appears to be the root of Issue #1491.
+   */
+  mark_point();
+  path = "/storetest";
+
+  /* NOTE: Yes, this is a surprising expectation; it has to do with the
+   * necessary fixes for Issue #1491.  Sigh.
+   */
+  expected = "/store/storetest";
+  res = vroot_path_lookup(p, vpath, vpathsz, path, 0, NULL);
+  ck_assert_msg(res >= 0, "Failed to lookup vpath for '%s': %s", path,
+    strerror(errno));
+  ck_assert_msg(strcmp(vpath, expected) == 0, "Expected '%s', got '%s'",
+    expected, vpath);
+
+  /* Set the new base. */
+  base = "/store/store";
+  baselen = strlen(base);
+
+  mark_point();
+  res = vroot_path_set_base(base, baselen);
+  ck_assert_msg(res == 0, "Failed to set base '%s': %s", base, strerror(errno));
+
+  /* Start with an absolute path that matches the base. */
+  mark_point();
+  path = base;
+
+  /* NOTE: Yes, this is a surprising expectation; it has to do with the
+   * necessary fixes for Issue #1491.  Sigh.  This is starting to look a little
+   * ridiculous.
+   */
+  expected = "/store/store/store/store";
+  res = vroot_path_lookup(p, vpath, vpathsz, path, 0, NULL);
+  ck_assert_msg(res >= 0, "Failed to lookup vpath for '%s': %s", path,
+    strerror(errno));
+  ck_assert_msg(strcmp(vpath, expected) == 0, "Expected '%s', got '%s'",
+    expected, vpath);
+
+  /* Then try a relative path whose name matches the base, sans the leading
+   * path delimiter.
+   */
+  mark_point();
+  path = "store";
+  expected = "/store";
+  res = vroot_path_lookup(p, vpath, vpathsz, path, 0, NULL);
+  ck_assert_msg(res >= 0, "Failed to lookup vpath for '%s': %s", path,
+    strerror(errno));
+  ck_assert_msg(strcmp(vpath, expected) == 0, "Expected '%s', got '%s'",
+    expected, vpath);
+
+  /* Next, try a relative path for a file whose name starts with that of
+   * the base.
+   */
+  mark_point();
+  path = "storetest";
+  expected = "/storetest";
+  res = vroot_path_lookup(p, vpath, vpathsz, path, 0, NULL);
+  ck_assert_msg(res >= 0, "Failed to lookup vpath for '%s': %s", path,
+    strerror(errno));
+  ck_assert_msg(strcmp(vpath, expected) == 0, "Expected '%s', got '%s'",
+    expected, vpath);
+
+  /* Next, use an absolute path for a file whose name starts with that of the
+   * base; this appears to be the root of Issue #1491.
+   */
+  mark_point();
+  path = "/storetest";
+
+  /* NOTE: Yes, this is a surprising expectation; it has to do with the
+   * necessary fixes for Issue #1491.  Sigh.
+   */
+  expected = "/store/store/storetest";
+  res = vroot_path_lookup(p, vpath, vpathsz, path, 0, NULL);
+  ck_assert_msg(res >= 0, "Failed to lookup vpath for '%s': %s", path,
+    strerror(errno));
+  ck_assert_msg(strcmp(vpath, expected) == 0, "Expected '%s', got '%s'",
+    expected, vpath);
+
+  /* Clear the base, using an empty string. */
+  mark_point();
+  path = "";
+  res = vroot_path_set_base(path, 0);
+  ck_assert_msg(res == 0, "Failed to set empty path as base: %s",
+    strerror(errno));
+}
+END_TEST
+
+/* TODO */
+START_TEST (path_lookup_with_alias_test) {
 }
 END_TEST
 
@@ -71,9 +398,14 @@ Suite *tests_get_path_suite(void) {
 
   tcase_add_checked_fixture(testcase, set_up, tear_down);
 
+  tcase_add_test(testcase, path_have_base_test);
+  tcase_add_test(testcase, path_get_base_test);
+  tcase_add_test(testcase, path_set_base_test);
   tcase_add_test(testcase, path_clean_test);
   tcase_add_test(testcase, realpath_test);
   tcase_add_test(testcase, path_lookup_test);
+  tcase_add_test(testcase, path_lookup_issue1491_test);
+  tcase_add_test(testcase, path_lookup_with_alias_test);
 
   suite_add_tcase(suite, testcase);
   return suite;


=====================================
t/lib/ProFTPD/Tests/Modules/mod_vroot.pm
=====================================
@@ -355,13 +355,18 @@ my $TESTS = {
     test_class => [qw(bug forking)],
   },
 
-  # See:
-  #  https://github.com/proftpd/proftpd/issues/59
+  # See:  https://github.com/proftpd/proftpd/issues/59
   vroot_alias_enametoolong_bug59 => {
     order => ++$order,
     test_class => [qw(bug forking)],
   },
 
+  # See: https://github.com/proftpd/proftpd/issues/1491
+  vroot_root_paths_hidden_issue1491 => {
+    order => ++$order,
+    test_class => [qw(bug forking)],
+  },
+
 };
 
 sub new {
@@ -458,6 +463,7 @@ sub vroot_engine {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -719,6 +725,7 @@ sub vroot_anon {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -1015,6 +1022,7 @@ sub vroot_anon_limit_write_allow_stor {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -1172,6 +1180,7 @@ sub vroot_symlink {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -1320,6 +1329,7 @@ sub vroot_symlink_eloop {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -1468,6 +1478,7 @@ sub vroot_opt_allow_symlinks_file {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -1642,6 +1653,7 @@ sub vroot_opt_allow_symlinks_dir_retr {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -1846,6 +1858,7 @@ sub vroot_opt_allow_symlinks_dir_stor_no_overwrite {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -2017,6 +2030,8 @@ sub vroot_opt_allow_symlinks_dir_stor {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
+
     AllowOverwrite => 'on',
 
     IfModules => {
@@ -2219,6 +2234,7 @@ sub vroot_opt_allow_symlinks_dir_cwd {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -2431,6 +2447,7 @@ sub vroot_dir_mkd {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     Directory => {
       # BUG: This should be $sub_dir.  But due to how mod_vroot currently
@@ -2540,12 +2557,12 @@ sub vroot_dir_mkd {
       $self->assert($have_smkdir_line,
         test_msg("Did not find expected 'fsio' channel TraceLog line in $setup->{log_file}"));
 
-      if ($line =~ /UID (\d+)/) {
+      if ($line =~ /UID (\S+),/) {
         my $smkdir_uid = $1;
 
         if ($< == 0) {
-          $self->assert($smkdir_uid == 0,
-            test_msg("Expected UID 0, got $smkdir_uid"));
+          $self->assert($smkdir_uid == 0 || $smkdir_uid == -1,
+            test_msg("Expected UID 0 or -1, got $smkdir_uid"));
         }
 
       } else {
@@ -2612,6 +2629,7 @@ sub vroot_server_root {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -2858,6 +2876,7 @@ sub vroot_server_root_mkd {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -3025,6 +3044,7 @@ sub vroot_alias_file_list {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -3218,6 +3238,7 @@ sub vroot_alias_file_list_multi {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => [
@@ -3377,6 +3398,7 @@ sub vroot_alias_file_retr {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -3482,6 +3504,7 @@ sub vroot_alias_file_stor_no_overwrite {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -3584,6 +3607,7 @@ sub vroot_alias_file_stor {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     AllowOverwrite => 'on',
 
@@ -3726,6 +3750,7 @@ sub vroot_alias_file_dele {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -3874,6 +3899,7 @@ sub vroot_alias_file_mlsd {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -4021,6 +4047,7 @@ sub vroot_alias_file_mlst {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -4162,6 +4189,7 @@ sub vroot_alias_dup_same_name {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -4348,6 +4376,7 @@ sub vroot_alias_dup_colliding_aliases {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => [
@@ -4536,6 +4565,7 @@ sub vroot_alias_delete_source {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -4724,6 +4754,7 @@ sub vroot_alias_no_source {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -4901,6 +4932,7 @@ sub vroot_alias_dir_list_no_trailing_slash {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -5080,6 +5112,7 @@ sub vroot_alias_dir_list_with_trailing_slash {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -5283,6 +5316,7 @@ sub vroot_alias_dir_list_from_above {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -5481,6 +5515,7 @@ sub vroot_alias_dir_cwd_list {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -5644,6 +5679,7 @@ sub vroot_alias_dir_cwd_stor {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -5805,6 +5841,7 @@ sub vroot_alias_dir_cwd_cdup {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -5975,6 +6012,7 @@ sub vroot_alias_dir_mkd {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -6114,6 +6152,7 @@ sub vroot_alias_dir_rmd {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -6277,6 +6316,7 @@ sub vroot_alias_dir_cwd_mlsd {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -6477,6 +6517,7 @@ sub vroot_alias_dir_mlsd_from_above {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -6669,6 +6710,7 @@ sub vroot_alias_dir_outside_root_cwd_mlsd {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -6884,6 +6926,7 @@ sub vroot_alias_dir_outside_root_cwd_mlsd_cwd_ls {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -7091,6 +7134,7 @@ sub vroot_alias_dir_mlst {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -7196,6 +7240,7 @@ sub vroot_alias_dir_list_multi_issue22 {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => [
@@ -7401,6 +7446,7 @@ sub vroot_alias_dir_mlsd_multi_issue22 {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => [
@@ -7605,6 +7651,7 @@ sub vroot_alias_symlink_list {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -7773,6 +7820,7 @@ sub vroot_alias_symlink_retr {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -7895,6 +7943,7 @@ sub vroot_alias_symlink_stor_no_overwrite {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -8016,6 +8065,7 @@ sub vroot_alias_symlink_stor {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     AllowOverwrite => 'on',
 
@@ -8173,6 +8223,7 @@ sub vroot_alias_symlink_mlsd {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -8336,6 +8387,7 @@ sub vroot_alias_symlink_mlst {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -8479,6 +8531,7 @@ sub vroot_alias_ifuser {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -8683,6 +8736,7 @@ sub vroot_alias_ifgroup {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -8888,6 +8942,7 @@ sub vroot_alias_ifgroup_list_stor {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -9161,6 +9216,7 @@ sub vroot_alias_ifclass {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -9374,6 +9430,7 @@ sub vroot_showsymlinks_on {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     # ShowSymlinks is on by default, but explicitly list it here for
     # completeness
@@ -9549,6 +9606,7 @@ sub vroot_hiddenstores_on_double_dot {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     AllowOverwrite => 'on',
     HiddenStores => 'on',
@@ -9654,6 +9712,7 @@ sub vroot_mfmt {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -9772,6 +9831,7 @@ sub vroot_log_extlog_retr {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     LogFormat => 'custom "%f"',
     ExtendedLog => "$ext_log READ custom",
@@ -9927,6 +9987,7 @@ sub vroot_log_extlog_stor {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     LogFormat => 'custom "%f"',
     ExtendedLog => "$ext_log WRITE custom",
@@ -10052,6 +10113,7 @@ sub vroot_log_xferlog_retr {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     TransferLog => $xfer_log,
 
@@ -10239,6 +10301,7 @@ sub vroot_log_xferlog_stor {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     TransferLog => $xfer_log,
 
@@ -10418,6 +10481,7 @@ sub vroot_config_limit_write {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -10571,6 +10635,7 @@ sub vroot_config_deleteabortedstores_conn_aborted {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     HiddenStores => 'on',
     DeleteAbortedStores => 'on',
@@ -10691,6 +10756,7 @@ sub vroot_config_deleteabortedstores_cmd_aborted {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     HiddenStores => 'on',
     DeleteAbortedStores => 'on',
@@ -10851,6 +10917,7 @@ sub vroot_alias_var_u_file {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -11043,6 +11110,7 @@ sub vroot_alias_var_u_dir {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -11204,6 +11272,7 @@ sub vroot_alias_var_u_dir_with_stor_mff {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_vroot.c' => {
@@ -11436,6 +11505,8 @@ sub vroot_alias_var_u_symlink_dir {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
+
     ShowSymlinks => 'off',
 
     IfModules => {
@@ -11578,29 +11649,13 @@ sub vroot_alias_var_u_symlink_dir {
 sub vroot_alias_bad_src_dst_check_bug4 {
   my $self = shift;
   my $tmpdir = $self->{tmpdir};
-
-  my $config_file = "$tmpdir/vroot.conf";
-  my $pid_file = File::Spec->rel2abs("$tmpdir/vroot.pid");
-  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/vroot.scoreboard");
-
-  my $log_file = test_get_logfile();
-
-  my $auth_user_file = File::Spec->rel2abs("$tmpdir/vroot.passwd");
-  my $auth_group_file = File::Spec->rel2abs("$tmpdir/vroot.group");
-
-  my $user = 'proftpd';
-  my $passwd = 'test';
-  my $group = 'ftpd';
-  my $home_dir = File::Spec->rel2abs("$tmpdir/$user");
-  mkpath($home_dir);
-  my $uid = 500;
-  my $gid = 500;
+  my $setup = test_setup($tmpdir, 'vroot');
 
   # In order for the real /tmp/vroot.d directory to be visible, via
   # VRootAlias, within the vroot, the leading /tmp directory needs to
   # actually exist with the vroot.  In other words, the path needs to be
   # real, even if the leaf is virtual.
-  my $user_tmpdir = File::Spec->rel2abs("$home_dir/tmp");
+  my $user_tmpdir = File::Spec->rel2abs("$setup->{home_dir}/tmp");
   mkpath($user_tmpdir);
 
   my $test_dir = File::Spec->rel2abs("/tmp/vroot.d");
@@ -11617,34 +11672,32 @@ sub vroot_alias_bad_src_dst_check_bug4 {
   # Make sure that, if we're running as root, that the home directory has
   # permissions/privs set for the account we create
   if ($< == 0) {
-    unless (chmod(0755, $home_dir, $user_tmpdir, $test_dir)) {
-      die("Can't set perms on $home_dir to 0755: $!");
+    unless (chmod(0755, $user_tmpdir, $test_dir)) {
+      die("Can't set perms on $user_tmpdir to 0755: $!");
     }
 
-    unless (chown($uid, $gid, $home_dir, $user_tmpdir, $test_dir)) {
-      die("Can't set owner of $home_dir to $uid/$gid: $!");
+    unless (chown($setup->{uid}, $setup->{gid}, $user_tmpdir, $test_dir)) {
+      die("Can't set owner of $user_tmpdir to $setup->{uid}/$setup->{gid}: $!");
     }
   }
 
-  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
-    '/bin/bash');
-  auth_group_write($auth_group_file, $group, $gid, $user);
-
   my $config = {
-    PidFile => $pid_file,
-    ScoreboardFile => $scoreboard_file,
-    SystemLog => $log_file,
-    TraceLog => $log_file,
+    PidFile => $setup->{pid_file},
+    ScoreboardFile => $setup->{scoreboard_file},
+    SystemLog => $setup->{log_file},
+    TraceLog => $setup->{log_file},
     Trace => 'fsio:20 vroot:20',
 
-    AuthUserFile => $auth_user_file,
-    AuthGroupFile => $auth_group_file,
+    AuthUserFile => $setup->{auth_user_file},
+    AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
+
     ShowSymlinks => 'off',
 
     IfModules => {
       'mod_vroot.c' => {
         VRootEngine => 'on',
-        VRootLog => $log_file,
+        VRootLog => $setup->{log_file},
         DefaultRoot => '~',
 
         VRootAlias => "$test_dir ~/tmp/vroot.d",
@@ -11656,7 +11709,8 @@ sub vroot_alias_bad_src_dst_check_bug4 {
     },
   };
 
-  my ($port, $config_user, $config_group) = config_write($config_file, $config);
+  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+    $config);
 
   # Open pipes, for use between the parent and child processes.  Specifically,
   # the child will indicate when it's done with its test by writing a message
@@ -11673,20 +11727,21 @@ sub vroot_alias_bad_src_dst_check_bug4 {
   defined(my $pid = fork()) or die("Can't fork: $!");
   if ($pid) {
     eval {
+      # Allow for server startup
+      sleep(1);
+
       my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
-      $client->login($user, $passwd);
+      $client->login($setup->{user}, $setup->{passwd});
 
       my ($resp_code, $resp_msg) = $client->pwd();
 
-      my $expected;
-
-      $expected = 257;
+      my $expected = 257;
       $self->assert($expected == $resp_code,
-        test_msg("Expected $expected, got $resp_code"));
+        test_msg("Expected response code $expected, got $resp_code"));
 
       $expected = "\"/\" is the current directory";
       $self->assert($expected eq $resp_msg,
-        test_msg("Expected '$expected', got '$resp_msg'"));
+        test_msg("Expected response message '$expected', got '$resp_msg'"));
 
       $client->cwd('/tmp/vroot.d');
 
@@ -11700,6 +11755,10 @@ sub vroot_alias_bad_src_dst_check_bug4 {
       $conn->read($buf, 8192, 5);
       eval { $conn->close() };
 
+      if ($ENV{TEST_VERBOSE}) {
+        print STDERR "# response:\n$buf\n";
+      }
+
       # We have to be careful of the fact that readdir returns directory
       # entries in an unordered fashion.
       my $res = {};
@@ -11738,7 +11797,6 @@ sub vroot_alias_bad_src_dst_check_bug4 {
 
       $client->quit();
     };
-
     if ($@) {
       $ex = $@;
     }
@@ -11747,7 +11805,7 @@ sub vroot_alias_bad_src_dst_check_bug4 {
     $wfh->flush();
 
   } else {
-    eval { server_wait($config_file, $rfh) };
+    eval { server_wait($setup->{config_file}, $rfh) };
     if ($@) {
       warn($@);
       exit 1;
@@ -11757,18 +11815,10 @@ sub vroot_alias_bad_src_dst_check_bug4 {
   }
 
   # Stop server
-  server_stop($pid_file);
-
+  server_stop($setup->{pid_file});
   $self->assert_child_ok($pid);
 
-  if ($ex) {
-    test_append_logfile($log_file, $ex);
-    unlink($log_file);
-
-    die($ex);
-  }
-
-  unlink($log_file);
+  test_cleanup($setup->{log_file}, $ex);
 }
 
 sub vroot_alias_bad_alias_dirscan_bug5 {
@@ -11801,6 +11851,8 @@ sub vroot_alias_bad_alias_dirscan_bug5 {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
+
     ShowSymlinks => 'off',
 
     IfModules => {
@@ -11984,6 +12036,8 @@ sub vroot_alias_enametoolong_bug59 {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
+
     ShowSymlinks => 'off',
 
     IfModules => {
@@ -12112,4 +12166,202 @@ sub vroot_alias_enametoolong_bug59 {
   unlink($log_file);
 }
 
+sub vroot_root_paths_hidden_issue1491 {
+  my $self = shift;
+  my $tmpdir = $self->{tmpdir};
+  my $setup = test_setup($tmpdir, 'vroot');
+
+  # Note: the actual reproduction recipe for this issue requires the use
+  # of a single-component root, e.g. "/store".  However, I use the
+  # normal automatically generated temporary directory (of multiple path
+  # components) here, for the rest of the machinery; the `use_opt` variable
+  # can be used in the future to run this test using the short `/opt` directory
+  # as the DefaultRoot.  Doing so require that that `/opt` directory be
+  # created (and populated!) manually.
+
+  my $use_opt = 0;
+
+  my $root_dir;
+
+  if ($use_opt) {
+    $root_dir = File::Spec->rel2abs('/opt');
+
+  } else {
+    $root_dir = File::Spec->rel2abs("$tmpdir/opt");
+    mkpath($root_dir);
+
+    my $root_files = [qw(
+      not-opt
+      opt
+      optagain
+      opttest
+    )];
+
+    foreach my $root_file (@$root_files) {
+      my $path = File::Spec->rel2abs("$root_dir/$root_file");
+      next if -f $path;
+
+      if (open(my $fh, "> $path")) {
+        close($fh);
+
+      } else {
+        die("Can't open $path: $!");
+      }
+    }
+
+    if ($< == 0) {
+      unless (chmod(0755, $root_dir)) {
+        die("Can't set perms on $root_dir to 0755: $!");
+      }
+
+      unless (chown($setup->{uid}, $setup->{gid}, $root_dir)) {
+        die("Can't set owner of $root_dir to $setup->{uid}/$setup->{gid}: $!");
+      }
+    }
+  }
+
+  my $config = {
+    PidFile => $setup->{pid_file},
+    ScoreboardFile => $setup->{scoreboard_file},
+    SystemLog => $setup->{log_file},
+    TraceLog => $setup->{log_file},
+    Trace => 'fsio:20 vroot:20 vroot.path:20',
+
+    AuthUserFile => $setup->{auth_user_file},
+    AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
+
+    ShowSymlinks => 'off',
+
+    IfModules => {
+      'mod_delay.c' => {
+        DelayEngine => 'off',
+      },
+
+      'mod_vroot.c' => {
+        VRootEngine => 'on',
+        VRootLog => $setup->{log_file},
+        DefaultRoot => $root_dir,
+      },
+    },
+  };
+
+  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+    $config);
+
+  # Open pipes, for use between the parent and child processes.  Specifically,
+  # the child will indicate when it's done with its test by writing a message
+  # to the parent.
+  my ($rfh, $wfh);
+  unless (pipe($rfh, $wfh)) {
+    die("Can't open pipe: $!");
+  }
+
+  my $ex;
+
+  # Fork child
+  $self->handle_sigchld();
+  defined(my $pid = fork()) or die("Can't fork: $!");
+  if ($pid) {
+    eval {
+      # Allow for server startup
+      sleep(1);
+
+      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
+      $client->login($setup->{user}, $setup->{passwd});
+
+      my $conn = $client->list_raw();
+      unless ($conn) {
+        die("Failed to LIST: " . $client->response_code() . " " .
+          $client->response_msg());
+      }
+
+      my $buf;
+      $conn->read($buf, 8192, 30);
+      eval { $conn->close() };
+
+      my $resp_code = $client->response_code();
+      my $resp_msg = $client->response_msg();
+      $self->assert_transfer_ok($resp_code, $resp_msg);
+
+      $client->quit();
+
+      if ($ENV{TEST_VERBOSE}) {
+        print STDERR "# data:\n$buf\n";
+      }
+
+      # We have to be careful of the fact that readdir returns directory
+      # entries in an unordered fashion.
+      my $res = {};
+      my $lines = [split(/\n/, $buf)];
+      foreach my $line (@$lines) {
+        if ($line =~ /^\S+\s+\d+\s+\S+\s+\S+\s+.*?\s+(\S+)$/) {
+          $res->{$1} = 1;
+        }
+      }
+
+      unless (scalar(keys(%$res)) > 0) {
+        die("LIST data unexpectedly empty");
+      }
+
+      my $expected = {
+        'not-opt' => 1,
+        'opt' => 1,
+        'optagain' => 1,
+        'opttest' => 1,
+      };
+
+      my $ok = 1;
+      my $mismatch;
+      foreach my $name (keys(%$res)) {
+        unless (defined($expected->{$name})) {
+          $mismatch = $name;
+          $ok = 0;
+          last;
+        }
+      }
+
+      unless ($ok) {
+        die("Unexpected name '$mismatch' appeared in LIST data")
+      }
+
+      $ok = 1;
+
+      my $missing;
+      foreach my $name (keys(%$expected)) {
+        unless (defined($res->{$name})) {
+          $missing = $name;
+          $ok = 0;
+          last;
+        }
+      }
+
+      unless ($ok) {
+        die("Unexpected name '$missing' missing from LIST data")
+      }
+    };
+    if ($@) {
+      $ex = $@;
+    }
+
+    $wfh->print("done\n");
+    $wfh->flush();
+
+  } else {
+    eval { server_wait($setup->{config_file}, $rfh) };
+    if ($@) {
+      warn($@);
+      exit 1;
+    }
+
+    exit 0;
+  }
+
+  # Stop server
+  server_stop($setup->{pid_file});
+  $self->assert_child_ok($pid);
+
+  test_cleanup($setup->{log_file}, $ex);
+}
+
 1;


=====================================
t/lib/ProFTPD/Tests/Modules/mod_vroot/sftp.pm
=====================================
@@ -272,6 +272,7 @@ sub vroot_alias_file_sftp_read {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [
@@ -413,6 +414,7 @@ sub vroot_alias_file_sftp_write_no_overwrite {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [
@@ -538,6 +540,7 @@ sub vroot_alias_file_sftp_write {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     AllowOverwrite => 'on',
 
@@ -707,6 +710,7 @@ sub vroot_alias_file_sftp_stat {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [
@@ -885,6 +889,7 @@ sub vroot_alias_file_sftp_lstat {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [
@@ -1063,6 +1068,7 @@ sub vroot_alias_file_sftp_realpath {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [
@@ -1232,6 +1238,7 @@ sub vroot_alias_file_sftp_remove {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [
@@ -1416,6 +1423,7 @@ sub vroot_alias_dir_sftp_readdir {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [
@@ -1618,6 +1626,7 @@ sub vroot_alias_dir_sftp_rmdir {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [
@@ -1801,6 +1810,7 @@ sub vroot_alias_symlink_sftp_stat {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [
@@ -1995,6 +2005,7 @@ sub vroot_alias_symlink_sftp_lstat {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [
@@ -2189,6 +2200,7 @@ sub vroot_alias_symlink_sftp_realpath {
 
     AuthUserFile => $auth_user_file,
     AuthGroupFile => $auth_group_file,
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [
@@ -2320,6 +2332,7 @@ sub vroot_alias_file_scp_download {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [
@@ -2439,6 +2452,8 @@ sub vroot_alias_file_scp_upload {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
+
     AllowOverwrite => 'on',
 
     IfModules => {
@@ -2562,6 +2577,7 @@ sub vroot_sftp_log_extlog_retr {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     LogFormat => 'custom "%f"',
     ExtendedLog => "$ext_log READ custom",
@@ -2725,6 +2741,7 @@ sub vroot_sftp_log_xferlog_retr {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     TransferLog => $xfer_log,
 
@@ -2906,6 +2923,7 @@ sub vroot_sftp_log_extlog_stor {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     LogFormat => 'custom "%f"',
     ExtendedLog => "$ext_log WRITE custom",
@@ -3053,6 +3071,7 @@ sub vroot_sftp_log_xferlog_stor {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     TransferLog => $xfer_log,
 
@@ -3238,6 +3257,7 @@ sub vroot_scp_log_extlog_retr {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     LogFormat => 'custom "%f"',
     ExtendedLog => "$ext_log READ custom",
@@ -3382,6 +3402,7 @@ sub vroot_scp_log_xferlog_retr {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     TransferLog => $xfer_log,
 
@@ -3544,6 +3565,7 @@ sub vroot_scp_log_extlog_stor {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     LogFormat => 'custom "%f"',
     ExtendedLog => "$ext_log WRITE custom",
@@ -3678,6 +3700,7 @@ sub vroot_scp_log_xferlog_stor {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     TransferLog => $xfer_log,
 
@@ -3870,6 +3893,7 @@ sub vroot_alias_dir_sftp_publickey_issue30 {
 
     AuthUserFile => $setup->{auth_user_file},
     AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
 
     IfModules => {
       'mod_sftp.c' => [



View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd-mod-vroot/-/compare/eb4365fca7bd47734d18d5d5a39f33e80bc511d6...9bdb030e43a65f14104c3afc642e86f753e03995

-- 
View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd-mod-vroot/-/compare/eb4365fca7bd47734d18d5d5a39f33e80bc511d6...9bdb030e43a65f14104c3afc642e86f753e03995
You're receiving this email because of your account on salsa.debian.org.




More information about the Pkg-proftpd-maintainers mailing list