[proftpd-mod-counter] 01/01: Finalize for release.

Hilmar Preuße hilmar-guest at moszumanska.debian.org
Fri Mar 16 21:13:57 GMT 2018


This is an automated email from the git hooks/post-receive script.

hilmar-guest pushed a commit to branch master
in repository proftpd-mod-counter.

commit c29e8d6e871e88c382e022f13615f55b235f9ef4
Author: Hilmar Preuße <hille42 at web.de>
Date:   Fri Mar 16 22:13:28 2018 +0100

    Finalize for release.
---
 README.md                                  |  14 +
 debian/changelog                           |   6 +-
 debian/compat                              |   2 +-
 debian/control                             |   6 +-
 debian/proftpd-mod-counter.docs            |   2 +
 debian/rules                               |   2 +-
 mod_counter.c                              | 140 +++--
 mod_counter.html                           |  11 +-
 t/lib/ProFTPD/Tests/Modules/mod_counter.pm | 945 ++++++++++++++++++++---------
 9 files changed, 785 insertions(+), 343 deletions(-)

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..58a1c06
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+proftpd-mod_counter
+===================
+
+Status
+------
+[![Build Status](https://travis-ci.org/Castaglia/proftpd-mod_counter.svg?branch=master)](https://travis-ci.org/Castaglia/proftpd-mod_counter)
+[![License](https://img.shields.io/badge/license-GPL-brightgreen.svg)](https://img.shields.io/badge/license-GPL-brightgreen.svg)
+
+Synopsis
+--------
+The `mod_counter` module for ProFTPD provides limits on the number
+of concurrent uploads/downloads for files.
+
+For further module documentation, see [mod_counter.html](https://htmlpreview.github.io/?https://github.com/Castaglia/proftpd-mod_counter/blob/master/mod_counter.html).
diff --git a/debian/changelog b/debian/changelog
index 788a989..558be70 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,7 @@
-proftpd-mod-counter (0.5-1) UNRELEASED; urgency=low
+proftpd-mod-counter (0.6.1-1) UNRELEASED; urgency=low
+
+  [ Hilmar Preuße ]
 
   * Initial release. (Closes: #653438)
 
- -- Mahyuddin Susanto <udienz at ubuntu.com>  Wed, 28 Dec 2011 17:09:19 +0700
+ -- Francesco Paolo Lovergine <frankie at debian.org>  Fri, 16 Mar 2018 22:08:51 +0100
diff --git a/debian/compat b/debian/compat
index 7f8f011..ec63514 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-7
+9
diff --git a/debian/control b/debian/control
index a7ae0a9..22f395b 100644
--- a/debian/control
+++ b/debian/control
@@ -1,13 +1,13 @@
 Source: proftpd-mod-counter
 Section: net
-Priority: extra
+Priority: optional
 Maintainer: ProFTPD Maintainance Team <pkg-proftpd-maintainers at lists.alioth.debian.org>
 Uploaders: Mahyuddin Susanto <udienz at ubuntu.com>,
            Francesco Paolo Lovergine <frankie at debian.org>
 Build-Depends: autotools-dev,
-               debhelper (>= 7.0.50~),
+               debhelper (>= 9~),
                proftpd-dev (>= 1.3.4~rc3-2~)
-Standards-Version: 3.9.2
+Standards-Version: 4.1.3
 Homepage: http://www.castaglia.org/proftpd/modules/mod_counter.html
 Vcs-Git: git://git.debian.org/pkg-proftpd/proftpd-mod-counter.git
 Vcs-Browser: http://git.debian.org/?p=pkg-proftpd/proftpd-mod-counter.git
diff --git a/debian/proftpd-mod-counter.docs b/debian/proftpd-mod-counter.docs
new file mode 100644
index 0000000..8d1524b
--- /dev/null
+++ b/debian/proftpd-mod-counter.docs
@@ -0,0 +1,2 @@
+mod_counter.html
+README.md
\ No newline at end of file
diff --git a/debian/rules b/debian/rules
index 5ca4db4..f05b002 100755
--- a/debian/rules
+++ b/debian/rules
@@ -5,7 +5,7 @@ MODULE_NAME=mod_counter
 DEBNAME=proftpd-mod-counter
 
 %:
-	dh $@
+	dh $@ --without python-support
 
 override_dh_auto_build:
 	DESTDIR=$(CURDIR)/debian/$(DEBNAME) prxs -c $(MODULE_NAME).c
diff --git a/mod_counter.c b/mod_counter.c
index 0788a5c..82051fc 100644
--- a/mod_counter.c
+++ b/mod_counter.c
@@ -1,7 +1,6 @@
 /*
  * ProFTPD: mod_counter -- a module for using counters to enforce per-file usage
- *
- * Copyright (c) 2004-2010 TJ Saunders
+ * Copyright (c) 2004-2017 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
@@ -24,8 +23,6 @@
  *
  * This is mod_counter, contrib software for proftpd 1.2.10rc1 and above.
  * For more information contact TJ Saunders <tj at castaglia.org>.
- *
- * $Id: mod_counter.c,v 1.6 2010/02/06 02:05:01 tj Exp tj $
  */
 
 #include "conf.h"
@@ -35,7 +32,7 @@
 #include <sys/ipc.h>
 #include <sys/sem.h>
 
-#define MOD_COUNTER_VERSION	"mod_counter/0.5"
+#define MOD_COUNTER_VERSION	"mod_counter/0.6.1"
 
 #if PROFTPD_VERSION_NUMBER < 0x0001030201
 # error "ProFTPD 1.3.2rc1 or later required"
@@ -63,6 +60,7 @@ struct counter_fh {
 static pool *counter_pool = NULL;
 
 static xaset_t *counter_fhs = NULL;
+static const char *counter_chroot_path = NULL;
 static const char *counter_curr_path = NULL;
 static int counter_curr_semid = -1;
 static int counter_engine = FALSE;
@@ -74,7 +72,9 @@ static int counter_pending = 0;
 #define COUNTER_HAVE_READER	0x01
 #define COUNTER_HAVE_WRITER	0x02
 
-#if (defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)) || defined(DARWIN9)
+#if (defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)) || \
+    defined(DARWIN9) || defined(DARWIN10) || defined(DARWIN11) || \
+    defined(DARWIN12) || defined(DARWIN13) || defined(DARWIN14)
 #else
 union semun {
   int val;
@@ -328,6 +328,8 @@ static int counter_file_write(pr_fh_t *fh, array_header *ids) {
   for (i = 0; i < ids->nelts; i++) {
     char buf[32];
 
+    pr_signals_handle();
+
     /* Skip any negative IDs.  This small hack allows for IDs to be
      * effectively removed from the list.
      */
@@ -366,27 +368,38 @@ static int counter_file_write(pr_fh_t *fh, array_header *ids) {
   return 0;
 }
 
+static char *counter_abs_path(pool *p, const char *path, int interpolate) {
+  const char *chroot_path;
+  char *abs_path;
+
+  chroot_path = session.chroot_path;
+  if (counter_chroot_path != NULL) {
+    session.chroot_path = counter_chroot_path;
+  }
+
+  abs_path = dir_abs_path(p, path, interpolate);
+  session.chroot_path = chroot_path;
+
+  return abs_path;
+}
+
 static pr_fh_t *counter_get_fh(pool *p, const char *path) {
   struct counter_fh *iter, *cfh = NULL;
   const char *abs_path;
-  size_t pathlen;
 
   /* Find the CounterFile handle to use for the given path, if any. */
-
   if (counter_fhs == NULL) {
     errno = ENOENT;
     return NULL;
   }
 
   if (session.chroot_path) {
-    abs_path = dir_abs_path(p, path, FALSE);
+    abs_path = counter_abs_path(p, path, FALSE);
 
   } else {
     abs_path = path;
   }
 
-  pathlen = strlen(abs_path);
-
   /* In order to handle globs, we do two passes.  On the first pass,
    * we look for the closest-matching glob area.  On the second pass,
    * we look for any closest-matching non-glob area.  This means that
@@ -395,6 +408,7 @@ static pr_fh_t *counter_get_fh(pool *p, const char *path) {
 
   for (iter = (struct counter_fh *) counter_fhs->xas_list; iter;
      iter = iter->next) {
+    pr_signals_handle();
 
     if (!iter->isglob) {
       continue;
@@ -619,12 +633,15 @@ MODRET set_counterengine(cmd_rec *cmd) {
 /* usage: CounterFile path */
 MODRET set_counterfile(cmd_rec *cmd) {
   config_rec *c;
+  const char *path;
 
   CHECK_ARGS(cmd, 1);
   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR);
 
-  if (*cmd->argv[1] != '/')
+  path = cmd->argv[1];
+  if (*path != '/') {
     CONF_ERROR(cmd, "must be an absolute path");
+  }
 
   /* In theory, we could open a filehandle on the configured path right
    * here, and fail if the file couldn't be created/opened.  Then we
@@ -637,7 +654,7 @@ MODRET set_counterfile(cmd_rec *cmd) {
    * where we know vhost to which the client connected.
    */
 
-  c = add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
+  c = add_config_param_str(cmd->argv[0], 1, path);
   c->flags |= CF_MERGEDOWN;
 
   return PR_HANDLED(cmd);
@@ -648,8 +665,9 @@ MODRET set_counterlog(cmd_rec *cmd) {
   CHECK_ARGS(cmd, 1);
   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
 
-  if (pr_fs_valid_path(cmd->argv[1]) < 0)
+  if (pr_fs_valid_path(cmd->argv[1]) < 0) {
     CONF_ERROR(cmd, "must be an absolute path");
+  }
 
   add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
   return PR_HANDLED(cmd);
@@ -692,25 +710,33 @@ MODRET counter_retr(cmd_rec *cmd) {
   config_rec *c;
   int res;
   pr_fh_t *fh;
+  const char *path;
 
-  if (!counter_engine)
+  if (counter_engine == FALSE) {
     return PR_DECLINED(cmd);
+  }
 
   c = find_config(CURRENT_CONF, CONF_PARAM, "CounterMaxReaders", FALSE);
   counter_max_readers = c ? *((int *) c->argv[0]) : COUNTER_DEFAULT_MAX_READERS;
 
-  if (counter_max_readers == 0)
+  if (counter_max_readers == 0) {
     return PR_DECLINED(cmd);
+  }
  
-  counter_curr_path = pr_table_get(cmd->notes, "mod_xfer.retr-path", NULL); 
-  if (counter_curr_path == NULL) {
+  path = pr_table_get(cmd->notes, "mod_xfer.retr-path", NULL);
+  if (path == NULL) {
     return PR_DECLINED(cmd);
   }
 
+  /* Note that for purposes of our semaphores, we need to use the absolute
+   * path.
+   */
+  counter_curr_path = counter_abs_path(cmd->tmp_pool, path, FALSE);
+
   fh = counter_get_fh(cmd->tmp_pool, counter_curr_path);
   if (fh == NULL) {
     (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
-      "%s: no CounterFile found for path '%s'", cmd->argv[0],
+      "%s: no CounterFile found for path '%s'", (char *) cmd->argv[0],
       counter_curr_path);
 
     /* No CounterFile configured/available for this path. */
@@ -736,7 +762,7 @@ MODRET counter_retr(cmd_rec *cmd) {
      * The download should be failed.
      */
     (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
-      "%s: max number of readers (%d) reached for '%s'", cmd->argv[0],
+      "%s: max number of readers (%d) reached for '%s'", (char *) cmd->argv[0],
       counter_max_readers, counter_curr_path);
     pr_response_add_err(R_450, _("%s: File busy"), cmd->arg);
     return PR_ERROR(cmd);
@@ -744,8 +770,8 @@ MODRET counter_retr(cmd_rec *cmd) {
 
   counter_pending |= COUNTER_HAVE_READER;
   (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
-    "%s: added reader counter for '%s' (semaphore ID %d)", cmd->argv[0],
-    counter_curr_path, counter_curr_semid);
+    "%s: added reader counter for '%s' (semaphore ID %d)",
+    (char *) cmd->argv[0], counter_curr_path, counter_curr_semid);
 
   return PR_DECLINED(cmd);
 }
@@ -783,7 +809,7 @@ MODRET counter_alter(cmd_rec *cmd) {
   fh = counter_get_fh(cmd->tmp_pool, counter_curr_path);
   if (fh == NULL) {
     (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
-      "%s: no CounterFile found for path '%s'", cmd->argv[0],
+      "%s: no CounterFile found for path '%s'", (char *) cmd->argv[0],
       counter_curr_path);
 
     /* No CounterFile configured/available for this path. */
@@ -809,7 +835,7 @@ MODRET counter_alter(cmd_rec *cmd) {
      * The upload should be failed.
      */
     (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
-      "%s: max number of writers (%d) reached for '%s'", cmd->argv[0],
+      "%s: max number of writers (%d) reached for '%s'", (char *) cmd->argv[0],
       counter_max_writers, counter_curr_path);
     pr_response_add_err(R_450, _("%s: File busy"), cmd->arg);
     return PR_ERROR(cmd);
@@ -817,8 +843,8 @@ MODRET counter_alter(cmd_rec *cmd) {
 
   counter_pending |= COUNTER_HAVE_WRITER;
   (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
-    "%s: added writer counter for '%s' (semaphore ID %d)", cmd->argv[0],
-    counter_curr_path, counter_curr_semid);
+    "%s: added writer counter for '%s' (semaphore ID %d)",
+    (char *) cmd->argv[0], counter_curr_path, counter_curr_semid);
 
   return PR_DECLINED(cmd);
 }
@@ -827,24 +853,33 @@ MODRET counter_stor(cmd_rec *cmd) {
   config_rec *c;
   int res;
   pr_fh_t *fh;
+  const char *path;
 
-  if (!counter_engine)
+  if (counter_engine == FALSE) {
     return PR_DECLINED(cmd);
+  }
 
   c = find_config(CURRENT_CONF, CONF_PARAM, "CounterMaxWriters", FALSE);
   counter_max_writers = c ? *((int *) c->argv[0]) : COUNTER_DEFAULT_MAX_WRITERS;
 
-  if (counter_max_writers == 0)
+  if (counter_max_writers == 0) {
     return PR_DECLINED(cmd);
+  }
 
-  counter_curr_path = pr_table_get(cmd->notes, "mod_xfer.store-path", NULL);
-  if (!counter_curr_path)
+  path = pr_table_get(cmd->notes, "mod_xfer.store-path", NULL);
+  if (path == NULL) {
     return PR_DECLINED(cmd);
+  }
+
+  /* Note that for purposes of our semaphores, we need to use the absolute
+   * path.
+   */
+  counter_curr_path = counter_abs_path(cmd->tmp_pool, path, FALSE);
 
   fh = counter_get_fh(cmd->tmp_pool, counter_curr_path);
   if (fh == NULL) {
     (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
-      "%s: no CounterFile found for path '%s'", cmd->argv[0],
+      "%s: no CounterFile found for path '%s'", (char *) cmd->argv[0],
       counter_curr_path);
 
     /* No CounterFile configured/available for this path. */
@@ -870,7 +905,7 @@ MODRET counter_stor(cmd_rec *cmd) {
      * The upload should be failed.
      */
     (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
-      "%s: max number of writers (%d) reached for '%s'", cmd->argv[0],
+      "%s: max number of writers (%d) reached for '%s'", (char *) cmd->argv[0],
       counter_max_writers, counter_curr_path);
     pr_response_add_err(R_450, _("%s: File busy"), cmd->arg);
     return PR_ERROR(cmd);
@@ -878,8 +913,8 @@ MODRET counter_stor(cmd_rec *cmd) {
 
   counter_pending |= COUNTER_HAVE_WRITER;
   (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
-    "%s: added writer counter for '%s' (semaphore ID %d)", cmd->argv[0],
-    counter_curr_path, counter_curr_semid);
+    "%s: added writer counter for '%s' (semaphore ID %d)",
+    (char *) cmd->argv[0], counter_curr_path, counter_curr_semid);
 
   return PR_DECLINED(cmd);
 }
@@ -896,7 +931,7 @@ MODRET counter_reader_done(cmd_rec *cmd) {
   fh = counter_get_fh(cmd->tmp_pool, counter_curr_path);
   if (fh == NULL) {
     (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
-      "%s: no CounterFile found for path '%s'", cmd->argv[0],
+      "%s: no CounterFile found for path '%s'", (char *) cmd->argv[0],
       counter_curr_path);
 
     /* No CounterFile configured/available for this path. */
@@ -943,7 +978,7 @@ MODRET counter_writer_done(cmd_rec *cmd) {
   fh = counter_get_fh(cmd->tmp_pool, counter_curr_path);
   if (fh == NULL) {
     (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
-      "%s: no CounterFile found for path '%s'", cmd->argv[0],
+      "%s: no CounterFile found for path '%s'", (char *) cmd->argv[0],
       counter_curr_path);
 
     /* No CounterFile configured/available for this path. */
@@ -993,11 +1028,16 @@ static void counter_mod_unload_ev(const void *event_data, void *user_data) {
 }
 #endif
 
+static void counter_chroot_ev(const void *event_data, void *user_data) {
+  counter_chroot_path = pstrdup(counter_pool, event_data);
+}
+
 static void counter_exit_ev(const void *event_data, void *user_data) {
   pr_fh_t *fh;
 
-  if (!counter_engine)
+  if (counter_engine == FALSE) {
     return;
+  }
 
   if (counter_curr_semid != -1 &&
       (counter_pending & COUNTER_HAVE_READER)) {
@@ -1049,15 +1089,16 @@ static int counter_sess_init(void) {
   config_rec *c;
 
   c = find_config(main_server->conf, CONF_PARAM, "CounterEngine", FALSE);
-  if (c &&
-      *((unsigned int *) c->argv[0]) == TRUE)
-    counter_engine = TRUE;
+  if (c != NULL) {
+    counter_engine = *((unsigned int *) c->argv[0]);
+  }
 
-  if (!counter_engine)
+  if (counter_engine == FALSE) {
     return 0;
+  }
 
   c = find_config(main_server->conf, CONF_PARAM, "CounterLog", FALSE);
-  if (c) {
+  if (c != NULL) {
     const char *path = c->argv[0];
 
     if (strcasecmp(path, "none") != 0) {
@@ -1082,8 +1123,8 @@ static int counter_sess_init(void) {
    * of a chroot.
    */
   c = find_config(main_server->conf, CONF_PARAM, "CounterFile", TRUE);
-  while (c) {
-    int xerrno;
+  while (c != NULL) {
+    int xerrno = 0;
     const char *area = NULL, *path;
     pr_fh_t *fh;
     struct counter_fh *cfh;
@@ -1114,7 +1155,7 @@ static int counter_sess_init(void) {
 
     PRIVS_ROOT
     fh = pr_fsio_open(path, O_RDWR|O_CREAT);
-    xerrno = xerrno;
+    xerrno = errno;
     PRIVS_RELINQUISH
 
     if (fh == NULL) {
@@ -1125,7 +1166,7 @@ static int counter_sess_init(void) {
       if (counter_fhs != NULL) {
         for (cfh = (struct counter_fh *) counter_fhs->xas_list; cfh;
             cfh = cfh->next) {
-          pr_fsio_close(cfh->fh);
+          (void) pr_fsio_close(cfh->fh);
         }
       }
 
@@ -1171,6 +1212,13 @@ static int counter_sess_init(void) {
 
   pr_event_register(&counter_module, "core.exit", counter_exit_ev, NULL);
 
+  /* If mod_vroot is present, we need to do a little more magic to counter
+   * the mod_vroot magic.
+   */
+  if (pr_module_exists("mod_vroot.c") == TRUE) {
+    pr_event_register(&counter_module, "core.chroot", counter_chroot_ev, NULL);
+  }
+
   return 0;
 }
 
diff --git a/mod_counter.html b/mod_counter.html
index 8197163..c49432c 100644
--- a/mod_counter.html
+++ b/mod_counter.html
@@ -1,6 +1,3 @@
-<!-- $Id: mod_case.html,v 1.4 2004/01/23 20:56:12 tj Exp tj $ -->
-<!-- $Source: /home/tj/proftpd/modules/doc/RCS/mod_case.html,v $ -->
-
 <html>
 <head>
 <title>ProFTPD module mod_counter</title>
@@ -210,13 +207,8 @@ can be downloading a given file at the same time, you would use the
 <p>
 <hr><br>
 
-Author: <i>$Author: tj $</i><br>
-Last Updated: <i>$Date: 2004/01/23 20:56:12 $</i><br>
-
-<br><hr>
-
 <font size=2><b><i>
-© Copyright 2004-2010 TJ Saunders<br>
+© Copyright 2004-2017 TJ Saunders<br>
  All Rights Reserved<br>
 </i></b></font>
 
@@ -224,4 +216,3 @@ Last Updated: <i>$Date: 2004/01/23 20:56:12 $</i><br>
 
 </body>
 </html>
-
diff --git a/t/lib/ProFTPD/Tests/Modules/mod_counter.pm b/t/lib/ProFTPD/Tests/Modules/mod_counter.pm
index 26aba91..270bf33 100644
--- a/t/lib/ProFTPD/Tests/Modules/mod_counter.pm
+++ b/t/lib/ProFTPD/Tests/Modules/mod_counter.pm
@@ -1,7 +1,7 @@
 package ProFTPD::Tests::Modules::mod_counter;
 
 use lib qw(t/lib);
-use base qw(Test::Unit::TestCase ProFTPD::TestSuite::Child);
+use base qw(ProFTPD::TestSuite::Child);
 use strict;
 
 use File::Copy;
@@ -112,6 +112,26 @@ my $TESTS = {
     test_class => [qw(forking)],
   },
 
+  counter_vroot_retr_max_readers_exceeded => {
+    order => ++$order,
+    test_class => [qw(forking mod_vroot)],
+  },
+
+  counter_vroot_retr_max_readers_exceeded_in_subdir => {
+    order => ++$order,
+    test_class => [qw(forking mod_vroot)],
+  },
+
+  counter_vroot_stor_max_writers_exceeded => {
+    order => ++$order,
+    test_class => [qw(forking mod_vroot)],
+  },
+
+  counter_vroot_stor_max_writers_exceeded_in_subdir => {
+    order => ++$order,
+    test_class => [qw(forking mod_vroot)],
+  },
+
 };
 
 sub new {
@@ -122,82 +142,29 @@ sub list_tests {
   return testsuite_get_runnable_tests($TESTS);
 }
 
-sub set_up {
-  my $self = shift;
-  $self->{tmpdir} = testsuite_get_tmp_dir();
-
-  # Create temporary scratch dir
-  eval { mkpath($self->{tmpdir}) };
-  if ($@) {
-    my $abs_path = File::Spec->rel2abs($self->{tmpdir});
-    die("Can't create dir $abs_path: $@");
-  }
-}
-
-sub tear_down {
-  my $self = shift;
-
-  # Remove temporary scratch dir
-  if ($self->{tmpdir}) {
-    eval { rmtree($self->{tmpdir}) };
-  }
-
-  undef $self;
-}
-
 sub counter_retr_max_readers_exceeded {
   my $self = shift;
   my $tmpdir = $self->{tmpdir};
+  my $setup = test_setup($tmpdir, 'counter');
   
-  my $config_file = "$tmpdir/counter.conf";
-  my $pid_file = File::Spec->rel2abs("$tmpdir/counter.pid");
-  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/counter.scoreboard");
-
-  my $log_file = File::Spec->rel2abs('tests.log');
-
-  my $auth_user_file = File::Spec->rel2abs("$tmpdir/counter.passwd");
-  my $auth_group_file = File::Spec->rel2abs("$tmpdir/counter.group");
-  
-  my $user = 'proftpd';
-  my $passwd = 'test';
-  my $home_dir = File::Spec->rel2abs($tmpdir);
-  my $uid = 500;
-  my $gid = 500;
-
-  # 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)) {
-      die("Can't set perms on $home_dir to 0755: $!");
-    }
-
-    unless (chown($uid, $gid, $home_dir)) {
-      die("Can't set owner of $home_dir to $uid/$gid: $!");
-    }
-  }
-
-  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
-    '/bin/bash');
-  auth_group_write($auth_group_file, 'ftpd', $gid, $user);
-
   my $counter_file = File::Spec->rel2abs("$tmpdir/counter.tab");
-
   my $test_file = 'counter.conf';
 
   my $config = {
-    PidFile => $pid_file,
-    ScoreboardFile => $scoreboard_file,
-    SystemLog => $log_file,
+    PidFile => $setup->{pid_file},
+    ScoreboardFile => $setup->{scoreboard_file},
+    SystemLog => $setup->{log_file},
+    TraceLog => $setup->{log_file},
+    Trace => 'counter:20',
 
-    AuthUserFile => $auth_user_file,
-    AuthGroupFile => $auth_group_file,
+    AuthUserFile => $setup->{auth_user_file},
+    AuthGroupFile => $setup->{auth_group_file},
 
     IfModules => {
       'mod_counter.c' => {
         CounterEngine => 'on',
-        CounterLog => $log_file,
+        CounterLog => $setup->{log_file},
         CounterFile => $counter_file,
-        CounterMaxReaders => 1,
       },
 
       'mod_delay.c' => {
@@ -206,7 +173,22 @@ sub counter_retr_max_readers_exceeded {
     },
   };
 
-  my ($port, $config_user, $config_group) = config_write($config_file, $config);
+  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+    $config);
+
+  if (open(my $fh, ">> $setup->{config_file}")) {
+    print $fh <<EOC;
+<Directory /test.d>
+  CounterMaxReaders 1
+</Directory>
+EOC
+    unless (close($fh)) {
+      die("Can't write $setup->{config_file}: $!");
+    }
+
+  } else {
+    die("Can't open $setup->{config_file}: $!");
+  }
 
   # 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
@@ -223,11 +205,11 @@ sub counter_retr_max_readers_exceeded {
   defined(my $pid = fork()) or die("Can't fork: $!");
   if ($pid) {
     eval {
-      my $client1 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
-      $client1->login($user, $passwd);
+      my $client1 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      $client1->login($setup->{user}, $setup->{passwd});
 
-      my $client2 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
-      $client2->login($user, $passwd);
+      my $client2 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      $client2->login($setup->{user}, $setup->{passwd});
 
       my $conn = $client1->retr_raw($test_file);
       unless ($conn) {
@@ -235,47 +217,35 @@ sub counter_retr_max_readers_exceeded {
           $client1->response_msg());
       }
 
-      my ($resp_code, $resp_msg);
-      my $expected;
-
       # Now, before we close this data connection, try to open another
       # data connection for the same file.
       my $conn2 = $client2->retr_raw($test_file);
       if ($conn2) {
         die("RETR $test_file succeeded unexpectedly");
+      }
 
-      } else {
-        $resp_code = $client2->response_code();
-        $resp_msg = $client2->response_msg();
+      my $resp_code = $client2->response_code();
+      my $resp_msg = $client2->response_msg();
 
-        $expected = 450;
-        $self->assert($expected == $resp_code,
-          test_msg("Expected $expected, got $resp_code"));
+      my $expected = 450;
+      $self->assert($expected == $resp_code,
+        "Expected response code $expected, got $resp_code");
 
-        $expected = "$test_file: File busy";
-        $self->assert($expected eq $resp_msg,
-          test_msg("Expected '$expected', got '$resp_msg'"));
-      }
+      $expected = "$test_file: File busy";
+      $self->assert($expected eq $resp_msg,
+        "Expected response message '$expected', got '$resp_msg'");
 
       my $buf;
       $conn->read($buf, 8192);
-      $conn->close();
+      eval { $conn->close() };
 
       $resp_code = $client1->response_code();
       $resp_msg = $client1->response_msg();
-
-      $expected = 226;
-      $self->assert($expected == $resp_code,
-        test_msg("Expected $expected, got $resp_code"));
-
-      $expected = "Transfer complete";
-      $self->assert($expected eq $resp_msg,
-        test_msg("Expected '$expected', got '$resp_msg'"));
+      $self->assert_transfer_ok($resp_code, $resp_msg);
 
       $client2->quit();
       $client1->quit();
     };
-
     if ($@) {
       $ex = $@;
     }
@@ -284,7 +254,7 @@ sub counter_retr_max_readers_exceeded {
     $wfh->flush();
 
   } else {
-    eval { server_wait($config_file, $rfh) };
+    eval { server_wait($setup->{config_file}, $rfh) };
     if ($@) {
       warn($@);
       exit 1;
@@ -294,15 +264,10 @@ sub counter_retr_max_readers_exceeded {
   }
 
   # Stop server
-  server_stop($pid_file);
-
+  server_stop($setup->{pid_file});
   $self->assert_child_ok($pid);
 
-  if ($ex) {
-    die($ex);
-  }
-
-  unlink($log_file);
+  test_cleanup($setup->{log_file}, $ex);
 }
 
 sub counter_stor_max_writers_exceeded {
@@ -411,27 +376,20 @@ sub counter_stor_max_writers_exceeded {
 
         $expected = 450;
         $self->assert($expected == $resp_code,
-          test_msg("Expected $expected, got $resp_code"));
+          "Expected response code $expected, got $resp_code");
 
         $expected = "$test_file: File busy";
         $self->assert($expected eq $resp_msg,
-          test_msg("Expected '$expected', got '$resp_msg'"));
+          "Expected response message '$expected', got '$resp_msg'");
       }
 
       my $buf = "Hello, World!\n";
       $conn->write($buf, length($buf));
-      $conn->close();
+      eval { $conn->close() };
 
       $resp_code = $client1->response_code();
       $resp_msg = $client1->response_msg();
-
-      $expected = 226;
-      $self->assert($expected == $resp_code,
-        test_msg("Expected $expected, got $resp_code"));
-
-      $expected = "Transfer complete";
-      $self->assert($expected eq $resp_msg,
-        test_msg("Expected '$expected', got '$resp_msg'"));
+      $self->assert_transfer_ok($resp_code, $resp_msg);
 
       $client2->quit();
       $client1->quit();
@@ -573,27 +531,20 @@ sub counter_appe_max_writers_exceeded {
 
         $expected = 450;
         $self->assert($expected == $resp_code,
-          test_msg("Expected $expected, got $resp_code"));
+          "Expected response code $expected, got $resp_code");
 
         $expected = "$test_file: File busy";
         $self->assert($expected eq $resp_msg,
-          test_msg("Expected '$expected', got '$resp_msg'"));
+          "Expected response message '$expected', got '$resp_msg'");
       }
 
       my $buf = "Hello, World!\n";
       $conn->write($buf, length($buf));
-      $conn->close();
+      eval { $conn->close() };
 
       $resp_code = $client1->response_code();
       $resp_msg = $client1->response_msg();
-
-      $expected = 226;
-      $self->assert($expected == $resp_code,
-        test_msg("Expected $expected, got $resp_code"));
-
-      $expected = "Transfer complete";
-      $self->assert($expected eq $resp_msg,
-        test_msg("Expected '$expected', got '$resp_msg'"));
+      $self->assert_transfer_ok($resp_code, $resp_msg);
 
       $client2->quit();
       $client1->quit();
@@ -734,32 +685,24 @@ sub counter_dele_max_writers_exceeded {
 
         $expected = 450;
         $self->assert($expected == $resp_code,
-          test_msg("Expected $expected, got $resp_code"));
+          "Expected response code $expected, got $resp_code");
 
         $expected = "$test_file: File busy";
         $self->assert($expected eq $resp_msg,
-          test_msg("Expected '$expected', got '$resp_msg'"));
+          "Expected response message '$expected', got '$resp_msg'");
       }
 
       my $buf = "Hello, World!\n";
       $conn->write($buf, length($buf));
-      $conn->close();
+      eval { $conn->close() };
 
       $resp_code = $client1->response_code();
       $resp_msg = $client1->response_msg();
-
-      $expected = 226;
-      $self->assert($expected == $resp_code,
-        test_msg("Expected $expected, got $resp_code"));
-
-      $expected = "Transfer complete";
-      $self->assert($expected eq $resp_msg,
-        test_msg("Expected '$expected', got '$resp_msg'"));
+      $self->assert_transfer_ok($resp_code, $resp_msg);
 
       $client2->quit();
       $client1->quit();
     };
-
     if ($@) {
       $ex = $@;
     }
@@ -895,32 +838,24 @@ sub counter_rnfr_max_writers_exceeded {
 
         $expected = 450;
         $self->assert($expected == $resp_code,
-          test_msg("Expected $expected, got $resp_code"));
+          "Expected response code $expected, got $resp_code");
 
         $expected = "$test_file: File busy";
         $self->assert($expected eq $resp_msg,
-          test_msg("Expected '$expected', got '$resp_msg'"));
+          "Expected response message '$expected', got '$resp_msg'");
       }
 
       my $buf = "Hello, World!\n";
       $conn->write($buf, length($buf));
-      $conn->close();
+      eval { $conn->close() };
 
       $resp_code = $client1->response_code();
       $resp_msg = $client1->response_msg();
-
-      $expected = 226;
-      $self->assert($expected == $resp_code,
-        test_msg("Expected $expected, got $resp_code"));
-
-      $expected = "Transfer complete";
-      $self->assert($expected eq $resp_msg,
-        test_msg("Expected '$expected', got '$resp_msg'"));
+      $self->assert_transfer_ok($resp_code, $resp_msg);
 
       $client2->quit();
       $client1->quit();
     };
-
     if ($@) {
       $ex = $@;
     }
@@ -1067,32 +1002,24 @@ sub counter_rnto_max_writers_exceeded {
 
         $expected = 450;
         $self->assert($expected == $resp_code,
-          test_msg("Expected $expected, got $resp_code"));
+          "Expected response code $expected, got $resp_code");
 
         $expected = "$test_file: File busy";
         $self->assert($expected eq $resp_msg,
-          test_msg("Expected '$expected', got '$resp_msg'"));
+          "Expected response message '$expected', got '$resp_msg'");
       }
 
       my $buf = "Hello, World!\n";
       $conn->write($buf, length($buf));
-      $conn->close();
+      eval { $conn->close() };
 
       $resp_code = $client1->response_code();
       $resp_msg = $client1->response_msg();
-
-      $expected = 226;
-      $self->assert($expected == $resp_code,
-        test_msg("Expected $expected, got $resp_code"));
-
-      $expected = "Transfer complete";
-      $self->assert($expected eq $resp_msg,
-        test_msg("Expected '$expected', got '$resp_msg'"));
+      $self->assert_transfer_ok($resp_code, $resp_msg);
 
       $client2->quit();
       $client1->quit();
     };
-
     if ($@) {
       $ex = $@;
     }
@@ -1253,32 +1180,24 @@ EOC
 
         $expected = 450;
         $self->assert($expected == $resp_code,
-          test_msg("Expected $expected, got $resp_code"));
+          "Expected response code $expected, got $resp_code");
 
         $expected = "$test_file: File busy";
         $self->assert($expected eq $resp_msg,
-          test_msg("Expected '$expected', got '$resp_msg'"));
+          "Expected response message '$expected', got '$resp_msg'");
       }
 
       my $buf;
       $conn->read($buf, 8192);
-      $conn->close();
+      eval { $conn->close() };
 
       $resp_code = $client1->response_code();
       $resp_msg = $client1->response_msg();
-
-      $expected = 226;
-      $self->assert($expected == $resp_code,
-        test_msg("Expected $expected, got $resp_code"));
-
-      $expected = "Transfer complete";
-      $self->assert($expected eq $resp_msg,
-        test_msg("Expected '$expected', got '$resp_msg'"));
+      $self->assert_transfer_ok($resp_code, $resp_msg);
 
       $client2->quit();
       $client1->quit();
     };
-
     if ($@) {
       $ex = $@;
     }
@@ -1440,32 +1359,24 @@ EOC
 
         $expected = 450;
         $self->assert($expected == $resp_code,
-          test_msg("Expected $expected, got $resp_code"));
+          "Expected response code $expected, got $resp_code");
 
         $expected = "$test_file: File busy";
         $self->assert($expected eq $resp_msg,
-          test_msg("Expected '$expected', got '$resp_msg'"));
+          "Expected response message '$expected', got '$resp_msg'");
       }
 
       my $buf;
       $conn->read($buf, 8192);
-      $conn->close();
+      eval { $conn->close() };
 
       $resp_code = $client1->response_code();
       $resp_msg = $client1->response_msg();
-
-      $expected = 226;
-      $self->assert($expected == $resp_code,
-        test_msg("Expected $expected, got $resp_code"));
-
-      $expected = "Transfer complete";
-      $self->assert($expected eq $resp_msg,
-        test_msg("Expected '$expected', got '$resp_msg'"));
+      $self->assert_transfer_ok($resp_code, $resp_msg);
 
       $client2->quit();
       $client1->quit();
     };
-
     if ($@) {
       $ex = $@;
     }
@@ -1632,32 +1543,24 @@ EOC
 
         $expected = 450;
         $self->assert($expected == $resp_code,
-          test_msg("Expected $expected, got $resp_code"));
+          "Expected response code $expected, got $resp_code");
 
         $expected = "$test_file: File busy";
         $self->assert($expected eq $resp_msg,
-          test_msg("Expected '$expected', got '$resp_msg'"));
+          "Expected response message '$expected', got '$resp_msg'");
       }
 
       my $buf;
       $conn->read($buf, 8192);
-      $conn->close();
+      eval { $conn->close() };
 
       $resp_code = $client1->response_code();
       $resp_msg = $client1->response_msg();
-
-      $expected = 226;
-      $self->assert($expected == $resp_code,
-        test_msg("Expected $expected, got $resp_code"));
-
-      $expected = "Transfer complete";
-      $self->assert($expected eq $resp_msg,
-        test_msg("Expected '$expected', got '$resp_msg'"));
+      $self->assert_transfer_ok($resp_code, $resp_msg);
 
       $client2->quit();
       $client1->quit();
     };
-
     if ($@) {
       $ex = $@;
     }
@@ -3196,62 +3099,44 @@ sub counter_closest_matching_file_using_anon_subdir {
 sub counter_closest_matching_file_using_globs {
   my $self = shift;
   my $tmpdir = $self->{tmpdir};
+  my $setup = test_setup($tmpdir, 'counter');
   
-  my $config_file = "$tmpdir/counter.conf";
-  my $pid_file = File::Spec->rel2abs("$tmpdir/counter.pid");
-  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/counter.scoreboard");
-
-  my $log_file = File::Spec->rel2abs('tests.log');
-
-  my $auth_user_file = File::Spec->rel2abs("$tmpdir/counter.passwd");
-  my $auth_group_file = File::Spec->rel2abs("$tmpdir/counter.group");
-  
-  my $user = 'proftpd';
-  my $passwd = 'test';
-  my $home_dir = File::Spec->rel2abs($tmpdir);
-  my $uid = 500;
-  my $gid = 500;
-
-  my $sub_dir = File::Spec->rel2abs("$home_dir/foo");
+  my $sub_dir = File::Spec->rel2abs("$tmpdir/foo");
   mkpath($sub_dir);
 
-  my $sub_sub_dir = File::Spec->rel2abs("$home_dir/foo/bar");
+  my $sub_sub_dir = File::Spec->rel2abs("$tmpdir/foo/bar");
   mkpath($sub_sub_dir);
 
-  # 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, $sub_dir, $sub_sub_dir)) {
-      die("Can't set perms on $home_dir to 0755: $!");
+    unless (chmod(0755, $sub_dir, $sub_sub_dir)) {
+      die("Can't set perms on $sub_dir to 0755: $!");
     }
 
-    unless (chown($uid, $gid, $home_dir, $sub_dir, $sub_sub_dir)) {
-      die("Can't set owner of $home_dir to $uid/$gid: $!");
+    unless (chown($setup->{uid}, $setup->{gid}, $sub_dir, $sub_sub_dir)) {
+      die("Can't set owner of $sub_dir 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, 'ftpd', $gid, $user);
-
-  my $toplevel_tab = File::Spec->rel2abs("$home_dir/counter.tab");
+  my $toplevel_tab = File::Spec->rel2abs("$tmpdir/counter.tab");
   my $subdir_tab = File::Spec->rel2abs("$sub_dir/counter.tab");
   my $subsubdir_tab = File::Spec->rel2abs("$sub_sub_dir/counter.tab");
 
   my $test_file = 'counter.conf';
 
   my $config = {
-    PidFile => $pid_file,
-    ScoreboardFile => $scoreboard_file,
-    SystemLog => $log_file,
+    PidFile => $setup->{pid_file},
+    ScoreboardFile => $setup->{scoreboard_file},
+    SystemLog => $setup->{log_file},
+    TraceLog => $setup->{log_file},
+    Trace => 'counter:20',
 
-    AuthUserFile => $auth_user_file,
-    AuthGroupFile => $auth_group_file,
+    AuthUserFile => $setup->{auth_user_file},
+    AuthGroupFile => $setup->{auth_group_file},
 
     IfModules => {
       'mod_counter.c' => {
         CounterEngine => 'on',
-        CounterLog => $log_file,
+        CounterLog => $setup->{log_file},
         CounterMaxReaders => 1,
       },
 
@@ -3261,9 +3146,10 @@ sub counter_closest_matching_file_using_globs {
     },
   };
 
-  my ($port, $config_user, $config_group) = config_write($config_file, $config);
+  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+    $config);
 
-  if (open(my $fh, ">> $config_file")) {
+  if (open(my $fh, ">> $setup->{config_file}")) {
     print $fh <<EOC;
 <Directory $sub_dir/*/>
   CounterFile $subsubdir_tab
@@ -3271,15 +3157,15 @@ sub counter_closest_matching_file_using_globs {
 EOC
 
     unless (close($fh)) {
-      die("Can't write $config_file: $!");
+      die("Can't write $setup->{config_file}: $!");
     }
 
   } else {
-    die("Can't open $config_file: $!");
+    die("Can't open $setup->{config_file}: $!");
   }
 
-  unless (copy($config_file, "$sub_sub_dir/counter.conf")) {
-    die("Can't copy $config_file to '$sub_sub_dir/counter.conf': $!");
+  unless (copy($setup->{config_file}, "$sub_sub_dir/counter.conf")) {
+    die("Can't copy $setup->{config_file} to '$sub_sub_dir/counter.conf': $!");
   }
 
   # Open pipes, for use between the parent and child processes.  Specifically,
@@ -3297,12 +3183,12 @@ EOC
   defined(my $pid = fork()) or die("Can't fork: $!");
   if ($pid) {
     eval {
-      my $client1 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
-      $client1->login($user, $passwd);
+      my $client1 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      $client1->login($setup->{user}, $setup->{passwd});
       $client1->cwd("foo/bar");
 
-      my $client2 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
-      $client2->login($user, $passwd);
+      my $client2 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      $client2->login($setup->{user}, $setup->{passwd});
       $client2->cwd("foo/bar");
    
       my $conn = $client1->retr_raw($test_file);
@@ -3311,47 +3197,35 @@ EOC
           $client1->response_msg());
       }
 
-      my ($resp_code, $resp_msg);
-      my $expected;
-
       # Now, before we close this data connection, try to open another
       # data connection for the same file.
       my $conn2 = $client2->retr_raw($test_file);
       if ($conn2) {
         die("RETR $test_file succeeded unexpectedly");
+      }
 
-      } else {
-        $resp_code = $client2->response_code();
-        $resp_msg = $client2->response_msg();
+      my $resp_code = $client2->response_code();
+      my $resp_msg = $client2->response_msg();
 
-        $expected = 450;
-        $self->assert($expected == $resp_code,
-          test_msg("Expected $expected, got $resp_code"));
+      my $expected = 450;
+      $self->assert($expected == $resp_code,
+        "Expected response code $expected, got $resp_code");
 
-        $expected = "$test_file: File busy";
-        $self->assert($expected eq $resp_msg,
-          test_msg("Expected '$expected', got '$resp_msg'"));
-      }
+      $expected = "$test_file: File busy";
+      $self->assert($expected eq $resp_msg,
+        "Expected response message '$expected', got '$resp_msg'");
 
       my $buf;
       $conn->read($buf, 8192);
-      $conn->close();
+      eval { $conn->close() };
 
       $resp_code = $client1->response_code();
       $resp_msg = $client1->response_msg();
-
-      $expected = 226;
-      $self->assert($expected == $resp_code,
-        test_msg("Expected $expected, got $resp_code"));
-
-      $expected = "Transfer complete";
-      $self->assert($expected eq $resp_msg,
-        test_msg("Expected '$expected', got '$resp_msg'"));
+      $self->assert_transfer_ok($resp_code, $resp_msg);
 
       $client2->quit();
       $client1->quit();
     };
-
     if ($@) {
       $ex = $@;
     }
@@ -3360,7 +3234,7 @@ EOC
     $wfh->flush();
 
   } else {
-    eval { server_wait($config_file, $rfh) };
+    eval { server_wait($setup->{config_file}, $rfh) };
     if ($@) {
       warn($@);
       exit 1;
@@ -3370,15 +3244,10 @@ EOC
   }
 
   # Stop server
-  server_stop($pid_file);
-
+  server_stop($setup->{pid_file});
   $self->assert_child_ok($pid);
 
-  if ($ex) {
-    die($ex);
-  }
-
-  unlink($log_file);
+  test_cleanup($setup->{log_file}, $ex);
 }
 
 sub counter_closest_matching_file_using_globs_and_exact {
@@ -3517,32 +3386,24 @@ EOC
 
         $expected = 450;
         $self->assert($expected == $resp_code,
-          test_msg("Expected $expected, got $resp_code"));
+          "Expected response code $expected, got $resp_code");
 
         $expected = "$test_file: File busy";
         $self->assert($expected eq $resp_msg,
-          test_msg("Expected '$expected', got '$resp_msg'"));
+          "Expected response message '$expected', got '$resp_msg'");
       }
 
       my $buf;
       $conn->read($buf, 8192);
-      $conn->close();
+      eval { $conn->close() };
 
       $resp_code = $client1->response_code();
       $resp_msg = $client1->response_msg();
-
-      $expected = 226;
-      $self->assert($expected == $resp_code,
-        test_msg("Expected $expected, got $resp_code"));
-
-      $expected = "Transfer complete";
-      $self->assert($expected eq $resp_msg,
-        test_msg("Expected '$expected', got '$resp_msg'"));
+      $self->assert_transfer_ok($resp_code, $resp_msg);
 
       $client2->quit();
       $client1->quit();
     };
-
     if ($@) {
       $ex = $@;
     }
@@ -3572,4 +3433,528 @@ EOC
   unlink($log_file);
 }
 
+sub counter_vroot_retr_max_readers_exceeded {
+  my $self = shift;
+  my $tmpdir = $self->{tmpdir};
+  my $setup = test_setup($tmpdir, 'counter');
+
+  my $counter_file = File::Spec->rel2abs("$tmpdir/counter.tab");
+
+  my $test_file = File::Spec->rel2abs("$tmpdir/test.dat");
+  if (open(my $fh, "> $test_file")) {
+    print $fh "Hello, World!\n";
+    unless (close($fh)) {
+      die("Can't write $test_file: $!");
+    }
+
+  } else {
+    die("Can't open $test_file: $!");
+  }
+
+  my $config = {
+    PidFile => $setup->{pid_file},
+    ScoreboardFile => $setup->{scoreboard_file},
+    SystemLog => $setup->{log_file},
+    TraceLog => $setup->{log_file},
+    Trace => 'counter:20 vroot:20 vroot.fsio:20',
+
+    AuthUserFile => $setup->{auth_user_file},
+    AuthGroupFile => $setup->{auth_group_file},
+    AllowOverwrite => 'on',
+    DefaultRoot => '~',
+
+    IfModules => {
+      'mod_counter.c' => {
+        CounterEngine => 'on',
+        CounterLog => $setup->{log_file},
+        CounterFile => $counter_file,
+        CounterMaxReaders => 1,
+      },
+
+      'mod_delay.c' => {
+        DelayEngine => 'off',
+      },
+
+      'mod_vroot.c' => {
+        VRootEngine => 'on',
+      },
+    },
+  };
+
+  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 {
+      my $client1 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      $client1->login($setup->{user}, $setup->{passwd});
+
+      my $client2 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      $client2->login($setup->{user}, $setup->{passwd});
+
+      my $conn = $client1->retr_raw('test.dat');
+      unless ($conn) {
+        die("Failed to RETR test.d: " . $client1->response_code() . " " .
+          $client1->response_msg());
+      }
+
+      # Now, before we close this data connection, try to open another
+      # data connection for the same file.
+      my $conn2 = $client2->retr_raw('test.dat');
+      if ($conn2) {
+        die("RETR test.dat succeeded unexpectedly");
+      }
+
+      my $resp_code = $client2->response_code();
+      my $resp_msg = $client2->response_msg();
+
+      my $expected = 450;
+      $self->assert($expected == $resp_code,
+        "Expected response code $expected, got $resp_code");
+
+      $expected = "test.dat: File busy";
+      $self->assert($expected eq $resp_msg,
+        "Expected response message '$expected', got '$resp_msg'");
+
+      my $buf;
+      $conn->read($buf, 8192, 15);
+      eval { $conn->close() };
+
+      $resp_code = $client1->response_code();
+      $resp_msg = $client1->response_msg();
+      $self->assert_transfer_ok($resp_code, $resp_msg);
+
+      $client2->quit();
+      $client1->quit();
+    };
+    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);
+}
+
+sub counter_vroot_retr_max_readers_exceeded_in_subdir {
+  my $self = shift;
+  my $tmpdir = $self->{tmpdir};
+  my $setup = test_setup($tmpdir, 'counter');
+
+  my $counter_file = File::Spec->rel2abs("$tmpdir/counter.tab");
+
+  my $sub_dir = File::Spec->rel2abs("$tmpdir/test.d");
+  mkpath($sub_dir);
+
+  my $test_file = File::Spec->rel2abs("$sub_dir/test.dat");
+  if (open(my $fh, "> $test_file")) {
+    print $fh "Hello, World!\n";
+    unless (close($fh)) {
+      die("Can't write $test_file: $!");
+    }
+
+  } else {
+    die("Can't open $test_file: $!");
+  }
+
+  my $config = {
+    PidFile => $setup->{pid_file},
+    ScoreboardFile => $setup->{scoreboard_file},
+    SystemLog => $setup->{log_file},
+    TraceLog => $setup->{log_file},
+    Trace => 'counter:20 vroot:20 vroot.fsio:20',
+
+    AuthUserFile => $setup->{auth_user_file},
+    AuthGroupFile => $setup->{auth_group_file},
+    AllowOverwrite => 'on',
+    DefaultRoot => '~',
+
+    IfModules => {
+      'mod_counter.c' => {
+        CounterEngine => 'on',
+        CounterLog => $setup->{log_file},
+        CounterFile => $counter_file,
+        CounterMaxReaders => 1,
+      },
+
+      'mod_delay.c' => {
+        DelayEngine => 'off',
+      },
+
+      'mod_vroot.c' => {
+        VRootEngine => 'on',
+      },
+    },
+  };
+
+  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 {
+      my $client1 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      $client1->login($setup->{user}, $setup->{passwd});
+
+      my $client2 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      $client2->login($setup->{user}, $setup->{passwd});
+
+      my $conn = $client1->retr_raw('test.d/test.dat');
+      unless ($conn) {
+        die("Failed to RETR test.d/test.d: " . $client1->response_code() . " " .
+          $client1->response_msg());
+      }
+
+      # Now, before we close this data connection, try to open another
+      # data connection for the same file.
+      my $conn2 = $client2->retr_raw('test.d/test.dat');
+      if ($conn2) {
+        die("RETR test.d/test.dat succeeded unexpectedly");
+      }
+
+      my $resp_code = $client2->response_code();
+      my $resp_msg = $client2->response_msg();
+
+      my $expected = 450;
+      $self->assert($expected == $resp_code,
+        "Expected response code $expected, got $resp_code");
+
+      $expected = "test.d/test.dat: File busy";
+      $self->assert($expected eq $resp_msg,
+        "Expected response message '$expected', got '$resp_msg'");
+
+      my $buf;
+      $conn->read($buf, 8192, 15);
+      eval { $conn->close() };
+
+      $resp_code = $client1->response_code();
+      $resp_msg = $client1->response_msg();
+      $self->assert_transfer_ok($resp_code, $resp_msg);
+
+      $client2->quit();
+      $client1->quit();
+    };
+    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);
+}
+
+sub counter_vroot_stor_max_writers_exceeded {
+  my $self = shift;
+  my $tmpdir = $self->{tmpdir};
+  my $setup = test_setup($tmpdir, 'counter');
+
+  my $counter_file = File::Spec->rel2abs("$tmpdir/counter.tab");
+  my $test_file = 'test.dat';
+
+  my $config = {
+    PidFile => $setup->{pid_file},
+    ScoreboardFile => $setup->{scoreboard_file},
+    SystemLog => $setup->{log_file},
+    TraceLog => $setup->{log_file},
+    Trace => 'counter:20 vroot:20 vroot.fsio:20',
+
+    AuthUserFile => $setup->{auth_user_file},
+    AuthGroupFile => $setup->{auth_group_file},
+    AllowOverwrite => 'on',
+    DefaultRoot => '~',
+
+    IfModules => {
+      'mod_counter.c' => {
+        CounterEngine => 'on',
+        CounterLog => $setup->{log_file},
+        CounterFile => $counter_file,
+        CounterMaxWriters => 1,
+      },
+
+      'mod_delay.c' => {
+        DelayEngine => 'off',
+      },
+
+      'mod_vroot.c' => {
+        VRootEngine => 'on',
+      },
+    },
+  };
+
+  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 {
+      my $client1 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      $client1->login($setup->{user}, $setup->{passwd});
+
+      my $client2 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      $client2->login($setup->{user}, $setup->{passwd});
+
+      my $conn = $client1->stor_raw($test_file);
+      unless ($conn) {
+        die("Failed to STOR: " . $client1->response_code() . " " .
+          $client1->response_msg());
+      }
+
+      # Now, before we close this data connection, try to open another
+      # data connection for the same file.
+      my $conn2 = $client2->stor_raw($test_file);
+      if ($conn2) {
+        die("STOR $test_file succeeded unexpectedly");
+      }
+
+      my $resp_code = $client2->response_code();
+      my $resp_msg = $client2->response_msg();
+
+      my $expected = 450;
+      $self->assert($expected == $resp_code,
+        "Expected response code $expected, got $resp_code");
+
+      $expected = "$test_file: File busy";
+      $self->assert($expected eq $resp_msg,
+        "Expected response message '$expected', got '$resp_msg'");
+
+      my $buf = 'Hello, World!\n';
+      $conn->write($buf, length($buf));
+      eval { $conn->close() };
+
+      $resp_code = $client1->response_code();
+      $resp_msg = $client1->response_msg();
+      $self->assert_transfer_ok($resp_code, $resp_msg);
+
+      $client2->quit();
+      $client1->quit();
+    };
+    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);
+}
+
+sub counter_vroot_stor_max_writers_exceeded_in_subdir {
+  my $self = shift;
+  my $tmpdir = $self->{tmpdir};
+  my $setup = test_setup($tmpdir, 'counter');
+
+  my $counter_file = File::Spec->rel2abs("$tmpdir/counter.tab");
+
+  my $sub_dir = File::Spec->rel2abs("$tmpdir/test.d");
+  mkpath($sub_dir);
+
+  my $test_file = 'test.d/test.dat';
+
+  my $config = {
+    PidFile => $setup->{pid_file},
+    ScoreboardFile => $setup->{scoreboard_file},
+    SystemLog => $setup->{log_file},
+    TraceLog => $setup->{log_file},
+    Trace => 'counter:20 vroot:20 vroot.fsio:20',
+
+    AuthUserFile => $setup->{auth_user_file},
+    AuthGroupFile => $setup->{auth_group_file},
+    AllowOverwrite => 'on',
+    DefaultRoot => '~',
+
+    IfModules => {
+      'mod_counter.c' => {
+        CounterEngine => 'on',
+        CounterLog => $setup->{log_file},
+        CounterFile => $counter_file,
+      },
+
+      'mod_delay.c' => {
+        DelayEngine => 'off',
+      },
+
+      'mod_vroot.c' => {
+        VRootEngine => 'on',
+      },
+    },
+  };
+
+  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+    $config);
+
+  if (open(my $fh, ">> $setup->{config_file}")) {
+    print $fh <<EOC;
+<Directory /test.d/>
+  CounterMaxWriters 1
+</Directory>
+EOC
+    unless (close($fh)) {
+      die("Can't write $setup->{config_file}: $!");
+    }
+
+  } else {
+    die("Can't open $setup->{config_file}: $!");
+  }
+
+  # 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 {
+      my $client1 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      $client1->login($setup->{user}, $setup->{passwd});
+
+      my $client2 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      $client2->login($setup->{user}, $setup->{passwd});
+
+      my $conn = $client1->stor_raw($test_file);
+      unless ($conn) {
+        die("Failed to STOR: " . $client1->response_code() . " " .
+          $client1->response_msg());
+      }
+
+      # Now, before we close this data connection, try to open another
+      # data connection for the same file.
+      my $conn2 = $client2->stor_raw($test_file);
+      if ($conn2) {
+        die("STOR $test_file succeeded unexpectedly");
+      }
+
+      my $resp_code = $client2->response_code();
+      my $resp_msg = $client2->response_msg();
+
+      my $expected = 450;
+      $self->assert($expected == $resp_code,
+        "Expected response code $expected, got $resp_code");
+
+      $expected = "$test_file: File busy";
+      $self->assert($expected eq $resp_msg,
+        "Expected response message '$expected', got '$resp_msg'");
+
+      my $buf = "Hello, World!\n";
+      $conn->write($buf, length($buf), 15);
+      eval { $conn->close() };
+
+      $resp_code = $client1->response_code();
+      $resp_msg = $client1->response_msg();
+      $self->assert_transfer_ok($resp_code, $resp_msg);
+
+      $client2->quit();
+      $client1->quit();
+    };
+    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;

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-proftpd/proftpd-mod-counter.git



More information about the Pkg-proftpd-maintainers mailing list