20553228 add proftpd dtrace provider from AK to Userland
authorTomas Klacko <tomas.klacko@oracle.com>
Wed, 11 Mar 2015 03:50:20 -0700
changeset 3932 1b7dd68f6aa9
parent 3930 acff78288302
child 3934 eb6d9a880b40
20553228 add proftpd dtrace provider from AK to Userland
components/proftpd/Makefile
components/proftpd/dtrace/Makefile
components/proftpd/dtrace/example.d
components/proftpd/dtrace/ftp.d
components/proftpd/dtrace/ftp_provider.d
components/proftpd/dtrace/ftp_provider_impl.c
components/proftpd/dtrace/ftp_provider_impl.h
components/proftpd/patches/000.proftpd-manpage.patch
components/proftpd/patches/001.mod_auth_unix_getgrp.patch
components/proftpd/patches/002.proftpd-configuration-html.patch
components/proftpd/patches/003.proftpd-pam.patch
components/proftpd/patches/004.proftpd-retry.patch
components/proftpd/patches/005.proftpd-error_code.patch
components/proftpd/patches/006.18845170.patch
components/proftpd/patches/007.19575647.patch
components/proftpd/patches/008.19596338.patch
components/proftpd/patches/009.19668629.patch
components/proftpd/patches/010.20393612.patch
components/proftpd/patches/011.19693019-proftpd-slow_ls.patch
components/proftpd/patches/012.20553228-dtrace-provider.patch
components/proftpd/patches/18845170.patch
components/proftpd/patches/19575647.patch
components/proftpd/patches/19596338.patch
components/proftpd/patches/19668629.patch
components/proftpd/patches/20393612.patch
components/proftpd/patches/mod_auth_unix_getgrp.patch
components/proftpd/patches/proftpd-configuration-html.patch
components/proftpd/patches/proftpd-error_code.patch
components/proftpd/patches/proftpd-manpage.patch
components/proftpd/patches/proftpd-pam.patch
components/proftpd/patches/proftpd-retry.patch
components/proftpd/patches/proftpd-slow_ls.patch
components/proftpd/proftpd.p5m
--- a/components/proftpd/Makefile	Tue Mar 10 11:12:15 2015 -0700
+++ b/components/proftpd/Makefile	Wed Mar 11 03:50:20 2015 -0700
@@ -60,7 +60,7 @@
 IPS_COMPONENT_VERSION=  $(COMPONENT_VERSION)
 
 # Remove -D_POSIX_PTHREAD_SEMANTICS and this comment after move to S12_45.
-CONFIGURE_OPTIONS +=	CFLAGS="$(CFLAGS) -I/usr/include/kerberosv5 -DHAVE_KRB5_H=1 -DKRB5_DLLIMP= -DHAVE__GETGRPSBYMEMBER -D_POSIX_PTHREAD_SEMANTICS"
+CONFIGURE_OPTIONS +=	CFLAGS="$(CFLAGS) -I/usr/include/kerberosv5 -DHAVE_KRB5_H=1 -DKRB5_DLLIMP= -DHAVE__GETGRPSBYMEMBER -D_POSIX_PTHREAD_SEMANTICS -D_SOLARIS_DTRACE"
 # Force immediate binding because of chroot().
 CONFIGURE_OPTIONS +=	LDFLAGS="-z guidance=nolazyload -z nolazyload -lbsm"
 CONFIGURE_OPTIONS +=	install_user=`id -nu`
@@ -79,7 +79,7 @@
 CONFIGURE_OPTIONS +=	--with-shared=mod_facl:mod_wrap:mod_tls:mod_auth_gss:mod_gss
 CONFIGURE_OPTIONS +=	--enable-buffer-size=16384
 
-# Copy Solaris modules and GSSAPI modules to proftpd source tree
+# Copy Solaris modules and GSSAPI modules to proftpd source tree.
 COMPONENT_PRE_CONFIGURE_ACTION = \
 	($(CP) mod_solaris_audit.c $(SOURCE_DIR)/contrib ; \
 	$(CP) mod_solaris_priv.c $(SOURCE_DIR)/contrib ; \
@@ -92,6 +92,10 @@
 	$(CP) mod_gss.html $(SOURCE_DIR)/doc/contrib ; \
 	$(CLONEY) $(SOURCE_DIR) $(@D))
 
+# Build Solaris dtrace object files and copy to proftpd build tree.
+COMPONENT_PRE_BUILD_ACTION = \
+	(cd dtrace && make CP="$(CP)" CC="$(CC)" BD="$(BUILD_DIR)/$(MACH32)")
+
 # proftpd configure and build is not ready for run out of the source tree
 CONFIGURE_SCRIPT =	$(@D)/configure
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/dtrace/Makefile	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,15 @@
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+#
+
+all:
+	$(CP) ftp_provider_impl.h $(BD)/modules
+	/usr/sbin/dtrace -xnolibs -C -h -s ftp_provider.d \
+		-o $(BD)/modules/ftp_provider.h
+	$(CC) -c ftp_provider_impl.c -I $(BD)/modules \
+		-o $(BD)/modules/ftp_provider_impl.o
+	/usr/sbin/dtrace -G -32 -xnolibs -C -s ftp_provider.d \
+		-o "$(BD)"/modules/ftp_provider.o \
+		$(BD)/modules/ftp_provider_impl.o
+	echo "modules/ftp_provider_impl.o" >> $(BD)/module-libs.txt
+	echo "modules/ftp_provider.o" >> $(BD)/module-libs.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/dtrace/example.d	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,28 @@
+#!/usr/sbin/dtrace -Zs
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#pragma D option quiet
+#pragma D option switchrate=10hz
+
+dtrace:::BEGIN
+{
+    printf("%-20s %-8s %9s %-5s %-6s %s\n", "CLIENT", "USER", "LAT(us)",
+        "DIR", "BYTES", "PATH");
+}
+
+ftp*:::transfer-start
+{
+    self->start = timestamp;
+}
+
+ftp*:::transfer-done
+/self->start/
+{
+    this->delta = (timestamp - self->start) / 1000;
+    printf("%-20s %-8s %9d %-5s %-6d %s\n", args[0]->ci_remote,
+    args[1]->fti_user, this->delta, args[1]->fti_cmd,
+    args[1]->fti_nbytes, args[1]->fti_pathname);
+    self->start = 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/dtrace/ftp.d	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#pragma D depends_on library net.d
+
+typedef struct ftpinfo {
+	string fti_user;                /* user name */
+	string fti_cmd;                 /* FTP command */
+	string fti_pathname;            /* path of file being operated upon */
+	uint64_t fti_nbytes;            /* bytes transferred, if any */
+	int fti_fd;                     /* fd for transfer, if any */
+} ftpinfo_t;
+
+/*
+ * This structure must match the definition of same in ftp_provider_impl.h.
+ */
+typedef struct ftpproto {
+	uint32_t ftp_user;              /* user name */
+	uint32_t ftp_cmd;               /* FTP command */
+	uint32_t ftp_pathname;          /* path of file being operated upon */
+	uint32_t ftp_raddr;             /* remote address, as IPv6 address */
+	uint32_t ftp_fd;                /* fd for transfer, if any */
+	uint32_t ftp_pad;               /* padding for copyin() */
+	uint64_t ftp_nbytes;            /* bytes transferred, if any */
+} ftpproto_t;
+
+#pragma D binding "1.6.1" translator
+translator conninfo_t <ftpproto_t *f> {
+	ci_protocol = "tcp";
+	ci_remote = copyinstr((uintptr_t)
+	*(uint32_t *)copyin((uintptr_t)&f->ftp_raddr, sizeof (uint32_t)));
+	ci_local = "<unknown>";
+};
+
+#pragma D binding "1.6.1" translator
+translator ftpinfo_t <ftpproto_t *f> {
+	fti_user = copyinstr((uintptr_t)
+	    *(uint32_t *)copyin((uintptr_t)&f->ftp_user, sizeof (uint32_t)));
+	fti_cmd = copyinstr((uintptr_t)
+	    *(uint32_t *)copyin((uintptr_t)&f->ftp_cmd, sizeof (uint32_t)));
+	fti_pathname = copyinstr((uintptr_t)
+	    *(uint32_t *)copyin((uintptr_t)&f->ftp_pathname,
+	    sizeof (uint32_t)));
+	fti_nbytes =
+	    *(uint64_t *)copyin((uintptr_t)&f->ftp_nbytes, sizeof (uint64_t));
+	fti_fd = *(uint32_t *)copyin((uintptr_t)&f->ftp_fd, sizeof (uint32_t));
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/dtrace/ftp_provider.d	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * We seem currently unable to depend properly on existing D libraries (like
+ * ftp.d). But the definitions for conninfo_t and ftpinfo_t are stored there
+ * (and have to be, since that's where the real translators live). So we're
+ * forced to define something here to satisfy dtrace(1M), but none of the
+ * definitions or translators here are actually used.
+ */
+typedef struct ftpinfo {
+	int dummy;
+} ftpinfo_t;
+
+typedef struct ftpproto {
+	int dummy;
+} ftpproto_t;
+
+typedef struct conninfo {
+	int dummy;
+} conninfo_t;
+
+translator conninfo_t <ftpproto_t *dp> {
+	dummy = 0;
+};
+
+translator ftpinfo_t <ftpproto_t *dp> {
+	dummy = 0;
+};
+
+provider ftp {
+	probe transfer__start(ftpproto_t *p) :
+	    (conninfo_t *p, ftpinfo_t *p);
+	probe transfer__done(ftpproto_t *p) :
+	    (conninfo_t *p, ftpinfo_t *p);
+};
+
+#pragma D attributes Evolving/Evolving/ISA provider ftp provider
+#pragma D attributes Private/Private/Unknown provider ftp module
+#pragma D attributes Private/Private/Unknown provider ftp function
+#pragma D attributes Private/Private/ISA provider ftp name
+#pragma D attributes Evolving/Evolving/ISA provider ftp args
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/dtrace/ftp_provider_impl.c	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <sys/int_types.h>
+
+#include "ftp_provider_impl.h"
+#include "ftp_provider.h"
+
+int
+ftp_transfer_start_enabled(void)
+{
+	return (FTP_TRANSFER_START_ENABLED());
+}
+
+int
+ftp_transfer_done_enabled(void)
+{
+	return (FTP_TRANSFER_DONE_ENABLED());
+}
+
+void
+ftp_transfer_start(struct ftpproto *ptr)
+{
+	FTP_TRANSFER_START(ptr);
+}
+
+void
+ftp_transfer_done(struct ftpproto *ptr)
+{
+	FTP_TRANSFER_DONE(ptr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/dtrace/ftp_provider_impl.h	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _FTP_PROVIDER_IMPL_H
+#define	_FTP_PROVIDER_IMPL_H
+
+/*
+ * This structure must match the definition of same in ftp.d.
+ */
+typedef struct ftpproto {
+	uint32_t ftp_user;	/* user name */
+	uint32_t ftp_cmd;	/* FTP command */
+	uint32_t ftp_pathname;  /* path of file being operated upon */
+	uint32_t ftp_raddr;	/* remote address, as IPv6 address */
+	uint32_t ftp_fd;	/* fd for transfer, if any */
+	uint32_t ftp_pad;	/* padding for copyin() */
+	uint64_t ftp_nbytes;	/* bytes transferred, if any */
+} ftpproto_t;
+
+#define	FTP_TRANSFER_PROTO(proto, fh, len) \
+do { \
+	bzero((proto), sizeof (struct ftpproto)); \
+	(proto)->ftp_user = (uint32_t)session.user; \
+	(proto)->ftp_cmd = (uint32_t)session.curr_cmd; \
+	(proto)->ftp_pathname = (uint32_t)((fh)->fh_path); \
+	(proto)->ftp_raddr = \
+	    (int32_t)pr_netaddr_get_ipstr(session.c->remote_addr); \
+	(proto)->ftp_fd = (uint32_t)((fh)->fh_fd); \
+	(proto)->ftp_nbytes = (len); \
+} while (0)
+
+extern int ftp_transfer_start_enabled(void);
+extern int ftp_transfer_done_enabled(void);
+extern void ftp_transfer_start(struct ftpproto *);
+extern void ftp_transfer_done(struct ftpproto *);
+
+#endif /* _FTP_PROVIDER_IMPL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/000.proftpd-manpage.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,17 @@
+--- a/src/proftpd.8.in
++++ b/src/proftpd.8.in
+@@ -100,10 +100,12 @@ This is enabled by default, if the \fB--enable-ipv6\fP configure option is
+ used.
+ .SH FILES
+ .PD 0
+-.B @SBINDIR@/proftpd
++.B /usr/lib/inet/proftpd
+ .br
+ .B @SYSCONFDIR@/proftpd.conf
+ .br
++.B @SYSCONFDIR@/ftpusers
++.br
+ .B @BINDIR@/ftpwho
+ .br
+ .B @BINDIR@/ftpcount
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/001.mod_auth_unix_getgrp.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,66 @@
+--- old/modules/mod_auth_unix.c	Fri May 25 08:31:35 2012
++++ new/modules/mod_auth_unix.c	Fri May 25 08:31:35 2012
+@@ -896,6 +896,13 @@
+ 
+   return gr ? mod_create_data(cmd, (void *) &gr->gr_gid) : PR_DECLINED(cmd);
+ }
++#ifdef HAVE__GETGRPSBYMEMBER
++  extern int _getgroupsbymember
++  (
++	const char* username, gid_t gid_array[],
++	int maxgids, int numgids
++  ); 
++#endif /* HAVE__GETGRPSBYMEMBER */
+ 
+ /* cmd->argv[0] = name
+  * cmd->argv[1] = (array_header **) group_ids
+@@ -1065,7 +1072,40 @@
+     }
+ 
+     free(ptr);
+-#else
++#else /* !HAVE_GETGRSET */
++#ifdef HAVE__GETGRPSBYMEMBER
++    gid_t group_ids[NGROUPS_MAX];
++    int ngroups = NGROUPS_MAX;
++    register unsigned int i;
++
++    pr_trace_msg("auth", 4,
++      "using _getgroupsbymember() to look up group membership");
++
++    memset(group_ids, 0, sizeof(group_ids));
++
++    group_ids[0]=pw->pw_gid;
++    ngroups=
++      _getgroupsbymember(pw->pw_name, group_ids, NGROUPS_MAX, 1);
++
++    if (ngroups < 0) {
++	pr_log_pri(PR_LOG_ERR,
++	    "_getgroupsbymember error: %s", strerror(errno));
++	return PR_DECLINED(cmd);
++    }
++
++    for (i = 0; i < ngroups; i++) {
++      gr = my_getgrgid(group_ids[i]);
++      if (gr) {
++        if (gids && pw->pw_gid != gr->gr_gid)
++          *((gid_t *) push_array(gids)) = gr->gr_gid;
++
++        if (groups && pw->pw_gid != gr->gr_gid) {
++          *((char **) push_array(groups)) = pstrdup(session.pool,
++            gr->gr_name);
++        }
++      }
++    }
++#else /* !HAVE__GETGRPSBYMEMBER */
+     char **gr_member = NULL;
+ 
+     /* This is where things get slow, expensive, and ugly.  Loop through
+@@ -1091,6 +1131,7 @@
+         }
+       }
+     }
++#endif /* !HAVE__GETGROUPSBYMEMBER */
+ #endif /* !HAVE_GETGRSET */
+   }
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/002.proftpd-configuration-html.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,12 @@
+--- a/doc/Configuration.html
++++ b/doc/Configuration.html
+@@ -10805,7 +10805,7 @@ CLASS="SYNOPSIS"
+ ></DT
+ ><DD
+ ><P
+->IdentLookups on</P
++>IdentLookups off</P
+ ></DD
+ ><DT
+ ><PRE
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/003.proftpd-pam.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,159 @@
+http://bugs.proftpd.org/show_bug.cgi?id=4070
+
+--- proftpd-1.3.3e/include/auth.h	Tue Sep  8 22:34:03 2009
++++ proftpd-1.3.3e-pam/include/auth.h	Tue May 24 10:37:40 2011
+@@ -59,6 +59,35 @@
+ /* Account has been disabled */
+ #define PR_AUTH_DISABLEDPWD		-5
+ 
++/* Insufficient credentials */
++#define PR_AUTH_CRED_INSUFF		-6
++
++/* Unavailable user credentials */
++#define PR_AUTH_CRED_UNAVAIL		-7
++
++/* Failure setting user credentials */
++#define PR_AUTH_CRED_ERR		-8
++
++/* Unavailable authentication service */
++#define PR_AUTH_UNAVAIL			-9
++
++/* Max retries reached */
++#define PR_AUTH_MAXTRIES		-10
++
++/* Initialization of authentization failed */
++#define PR_AUTH_INIT_FAIL		-11
++
++/* New auth token needed */
++#define PR_AUTH_NEWTOK			-12
++
++#define PR_AUTH_OPEN_ERR		-15
++#define PR_AUTH_SYMBOL_ERR		-16
++#define PR_AUTH_SERVICE_ERR		-17
++#define PR_AUTH_SYSTEM_ERR		-18
++#define PR_AUTH_BUF_ERR			-19
++#define PR_AUTH_CONV_ERR		-20
++#define PR_AUTH_PERM_DENIED		-21
++
+ void pr_auth_setpwent(pool *);
+ void pr_auth_endpwent(pool *);
+ void pr_auth_setgrent(pool *);
+--- proftpd-1.3.3e/modules/mod_auth.c	Mon Feb 21 03:36:38 2011
++++ proftpd-1.3.3e-pam/modules/mod_auth.c	Tue May 24 11:32:55 2011
+@@ -898,6 +898,44 @@
+           user);
+         goto auth_failure;
+ 
++      case PR_AUTH_CRED_INSUFF:
++        pr_log_auth(PR_LOG_NOTICE,
++          "USER %s (Login failed): Insufficient credentials.", origuser);
++        goto auth_failure;
++
++      case PR_AUTH_CRED_UNAVAIL:
++        pr_log_auth(PR_LOG_NOTICE, 
++          "USER %s (Login failed): Unavailable credentials.", origuser);
++        goto auth_failure;
++
++
++      case PR_AUTH_CRED_ERR:
++        pr_log_auth(PR_LOG_NOTICE, 
++          "USER %s (Login failed): Failure setting user credentials.",
++          origuser);
++        goto auth_failure;
++
++      case PR_AUTH_UNAVAIL:
++        pr_log_auth(PR_LOG_NOTICE,
++          "USER %s (Login failed): Unavailable authentication service.", user);
++        goto auth_failure;
++
++      case PR_AUTH_MAXTRIES:
++        pr_log_auth(PR_LOG_NOTICE,
++          "USER %s (Login failed): Max retries reached.", user);
++        goto auth_failure;
++
++      case PR_AUTH_INIT_FAIL:
++        pr_log_auth(PR_LOG_NOTICE,
++          "USER %s (Login failed): Authentization initialization failed.",
++          origuser);
++        goto auth_failure;
++
++      case PR_AUTH_NEWTOK:
++        pr_log_auth(PR_LOG_NOTICE,
++          "USER %s (Login failed): New authentication token needed.", user);
++        goto auth_failure;
++
+       default:
+         break;
+     };
+--- proftpd-1.3.3e/modules/mod_auth_pam.c	Thu Mar  5 06:24:06 2009
++++ proftpd-1.3.3e-pam/modules/mod_auth_pam.c	Tue May 24 10:28:58 2011
+@@ -349,6 +349,24 @@
+ 
+   if (pam_error != PAM_SUCCESS) {
+     switch (pam_error) {
++#ifdef PAM_CRED_INSUFFICIENT
++      case PAM_CRED_INSUFFICIENT:
++        retval = PR_AUTH_CRED_INSUFF;
++        break;
++#endif /* PAM_CRED_INSUFFICIENT */
++
++#ifdef PAM_AUTHINFO_UNAVAIL
++      case PAM_AUTHINFO_UNAVAIL:
++        retval = PR_AUTH_UNAVAIL;
++        break;
++#endif /* PAM_AUTHINFO_UNAVAIL */
++
++#ifdef PAM_MAXTRIES
++      case PAM_MAXTRIES:
++        retval = PR_AUTH_MAXTRIES;
++        break;
++#endif /* PAM_MAXTRIES */
++
+       case PAM_USER_UNKNOWN:
+         retval = PR_AUTH_NOPWD;
+         break;
+@@ -373,6 +391,14 @@
+ 
+   if (pam_error != PAM_SUCCESS) {
+     switch (pam_error) {
++#ifdef PAM_NEW_AUTHTOK_REQD
++      case PAM_NEW_AUTHTOK_REQD:
++        pr_trace_msg(trace_channel, 8,
++          "account mgmt error: PAM_NEW_AUTH_REQD");
++        retval = PR_AUTH_NEWTOK;
++        break;
++#endif /* PAM_NEW_AUTHTOK_REQD */
++
+ #ifdef PAM_AUTHTOKEN_REQD
+       case PAM_AUTHTOKEN_REQD:
+         pr_trace_msg(trace_channel, 8,
+@@ -417,7 +443,7 @@
+     switch (pam_error) {
+       case PAM_SESSION_ERR:
+       default:
+-        retval = PR_AUTH_DISABLEDPWD;
++        retval = PR_AUTH_INIT_FAIL;
+         break;
+     }
+ 
+@@ -435,6 +461,20 @@
+ 
+   if (pam_error != PAM_SUCCESS) {
+     switch (pam_error) {
++#ifdef PAM_CRED_UNAVAIL
++      case PAM_CRED_UNAVAIL:
++        pr_trace_msg(trace_channel, 8, "credentials error: PAM_CRED_UNAVAIL");
++        retval = PR_AUTH_CRED_UNAVAIL;
++        break;
++#endif /* PAM_CRED_UNAVAIL */
++
++#ifdef PAM_CRED_ERR
++      case PAM_CRED_ERR:
++        pr_trace_msg(trace_channel, 8, "credentials error: PAM_CRED_ERR");
++        retval = PR_AUTH_CRED_ERR;
++        break;
++#endif /* PAM_CRED_ERR */
++
+       case PAM_CRED_EXPIRED:
+         pr_trace_msg(trace_channel, 8, "credentials error: PAM_CRED_EXPIRED");
+         retval = PR_AUTH_AGEPWD;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/004.proftpd-retry.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,48 @@
+--- a/src/data.c
++++ b/src/data.c
+@@ -337,6 +337,7 @@ static int data_pasv_open(char *reason, off_t size) {
+ static int data_active_open(char *reason, off_t size) {
+   conn_t *c;
+   int bind_port, rev;
++  int retries = 0;
+   pr_netaddr_t *bind_addr;
+   unsigned char *root_revoke = NULL;
+ 
+@@ -368,7 +369,9 @@ static int data_active_open(char *reason, off_t size) {
+     bind_port = INPORT_ANY;
+   }
+ 
+-  session.d = pr_inet_create_conn(session.pool, -1, bind_addr, bind_port, TRUE);
++  for (;;) {
++    session.d = pr_inet_create_conn(session.pool, -1, bind_addr, bind_port,
++      TRUE);
+ 
+   /* Default remote address to which to connect for an active transfer,
+    * if the client has not specified a different address via PORT/EPRT,
+@@ -416,6 +419,15 @@ static int data_active_open(char *reason, off_t size) {
+ 
+   if (pr_inet_connect(session.d->pool, session.d, &session.data_addr,
+       session.data_port) == -1) {
++
++    if (session.d->xerrno == EADDRINUSE && retries < 16) {
++      destroy_pool(session.d->pool);
++      pr_signals_handle();
++      /* Wait up to MSL to avoid TIME_WAIT. */
++      sleep(retries++);
++      continue;
++    }
++
+     pr_log_debug(DEBUG6,
+       "Error connecting to %s#%u for active data transfer: %s",
+       pr_netaddr_get_ipstr(&session.data_addr), session.data_port,
+@@ -427,6 +439,9 @@ static int data_active_open(char *reason, off_t size) {
+     destroy_pool(session.d->pool);
+     session.d = NULL;
+     return -1;
++  } else
++    break;
++
+   }
+ 
+   c = pr_inet_openrw(session.pool, session.d, NULL, PR_NETIO_STRM_DATA,
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/005.proftpd-error_code.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,466 @@
+diff --git a/include/dirtree.h b/include/dirtree.h
+index 0f41986..fe7b14b 100644
+--- a/include/dirtree.h
++++ b/include/dirtree.h
+@@ -126,6 +126,10 @@ typedef struct cmd_struc {
+   pr_table_t *notes;		/* Private data for passing/retaining between handlers */
+ 
+   int cmd_id;			/* Index into commands list, for faster comparisons */
++
++  int error_code;               /* Stores errno of failed file transfer
++                                 * commands. Required for Solaris auditing.
++                                 */
+ } cmd_rec;
+ 
+ struct config_struc {
+diff --git a/modules/mod_core.c b/modules/mod_core.c
+index e33ee11..f680748 100644
+--- a/modules/mod_core.c
++++ b/modules/mod_core.c
+@@ -4775,6 +4775,7 @@ MODRET core_rmd(cmd_rec *cmd) {
+   dir = dir_canonical_path(cmd->tmp_pool, dir);
+   if (dir == NULL) {
+     int xerrno = EINVAL;
++    cmd->error_code = EINVAL;
+ 
+     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
+ 
+@@ -4784,6 +4785,7 @@ MODRET core_rmd(cmd_rec *cmd) {
+ 
+   if (!dir_check_canon(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
+     int xerrno = EACCES;
++    cmd->error_code = EACCES;
+ 
+     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
+ 
+@@ -4793,6 +4795,7 @@ MODRET core_rmd(cmd_rec *cmd) {
+ 
+   if (pr_fsio_rmdir(dir) < 0) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
+       "error removing directory '%s': %s", cmd->argv[0], session.user,
+@@ -4849,6 +4852,7 @@ MODRET core_mkd(cmd_rec *cmd) {
+   dir = dir_canonical_path(cmd->tmp_pool, dir);
+   if (dir == NULL) {
+     int xerrno = EINVAL;
++    cmd->error_code = EINVAL;
+ 
+     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
+ 
+@@ -4858,6 +4862,7 @@ MODRET core_mkd(cmd_rec *cmd) {
+ 
+   if (!dir_check_canon(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
+     int xerrno = EACCES;
++    cmd->error_code = EACCES;
+ 
+     pr_log_debug(DEBUG8, "%s command denied by <Limit> config", cmd->argv[0]);
+     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
+@@ -4869,6 +4874,7 @@ MODRET core_mkd(cmd_rec *cmd) {
+   if (pr_fsio_smkdir(cmd->tmp_pool, dir, 0777, session.fsuid,
+       session.fsgid) < 0) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
+       "error making directory '%s': %s", cmd->argv[0], session.user,
+@@ -4915,6 +4921,7 @@ MODRET core_mdtm(cmd_rec *cmd) {
+       !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
+       pr_fsio_stat(path, &st) == -1) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
+ 
+@@ -5026,6 +5033,7 @@ MODRET core_dele(cmd_rec *cmd) {
+   path = dir_canonical_path(cmd->tmp_pool, path);
+   if (path == NULL) {
+     int xerrno = ENOENT;
++    cmd->error_code = ENOENT;
+ 
+     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
+ 
+@@ -5035,6 +5043,7 @@ MODRET core_dele(cmd_rec *cmd) {
+ 
+   if (!dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     pr_log_debug(DEBUG7, "deleting '%s' denied by <Limit> configuration", path);
+     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
+@@ -5051,6 +5060,7 @@ MODRET core_dele(cmd_rec *cmd) {
+   pr_fs_clear_cache();
+   if (pr_fsio_lstat(path, &st) < 0) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     pr_log_debug(DEBUG3, "unable to lstat '%s': %s", path, strerror(xerrno));
+     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
+@@ -5065,6 +5075,7 @@ MODRET core_dele(cmd_rec *cmd) {
+    */
+   if (S_ISDIR(st.st_mode)) {
+     int xerrno = EISDIR;
++    cmd->error_code = EISDIR;
+ 
+     (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
+       "error deleting '%s': %s", cmd->argv[0], session.user,
+@@ -5081,6 +5092,7 @@ MODRET core_dele(cmd_rec *cmd) {
+  
+   if (pr_fsio_unlink(path) < 0) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
+       "error deleting '%s': %s", cmd->argv[0], session.user,
+@@ -5165,6 +5177,7 @@ MODRET core_rnto(cmd_rec *cmd) {
+     pr_log_debug(DEBUG6, "AllowOverwrite denied permission for %s", path);
+     pr_response_add_err(R_550, _("%s: Rename permission denied"), cmd->arg);
+     errno = EACCES;
++    cmd->error_code = EACCES;
+     return PR_ERROR(cmd);
+   }
+ 
+@@ -5172,6 +5185,7 @@ MODRET core_rnto(cmd_rec *cmd) {
+       !dir_check_canon(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
+       pr_fsio_rename(session.xfer.path, path) == -1) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     if (xerrno != EXDEV) {
+       (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
+@@ -5223,6 +5237,7 @@ MODRET core_rnto(cmd_rec *cmd) {
+      */
+     if (pr_fs_copy_file(session.xfer.path, path) < 0) {
+       xerrno = errno;
++      cmd->error_code = errno;
+ 
+       (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
+         "error copying '%s' to '%s': %s", cmd->argv[0], session.user,
+@@ -5238,6 +5253,8 @@ MODRET core_rnto(cmd_rec *cmd) {
+ 
+     /* Once copied, unlink the original file. */
+     if (pr_fsio_unlink(session.xfer.path) < 0) {
++      cmd->error_code = errno;
++
+       pr_log_debug(DEBUG0, "error unlinking '%s': %s", session.xfer.path,
+         strerror(errno));
+     }
+@@ -5295,6 +5312,7 @@ MODRET core_rnfr(cmd_rec *cmd) {
+       !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
+       !exists(path)) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
+ 
+diff --git a/modules/mod_xfer.c b/modules/mod_xfer.c
+index bcfb487..41c597e 100644
+--- a/modules/mod_xfer.c
++++ b/modules/mod_xfer.c
+@@ -1190,6 +1190,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
+   if (cmd->argc < 2) {
+     pr_response_add_err(R_500, _("'%s' not understood"),
+       pr_cmd_get_displayable_str(cmd, NULL));
++    cmd->error_code = EINVAL;
+     errno = EINVAL;
+     return PR_ERROR(cmd);
+   }
+@@ -1200,6 +1201,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
+   if (!path ||
+       !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     pr_log_debug(DEBUG8, "%s %s denied by <Limit> configuration", cmd->argv[0],
+       cmd->arg);
+@@ -1232,6 +1234,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
+   if (xfer_check_limit(cmd) < 0) {
+     pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
+     errno = EPERM;
++    cmd->error_code = EPERM;
+     return PR_ERROR(cmd);
+   }
+ 
+@@ -1244,6 +1247,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
+     pr_log_debug(DEBUG6, "AllowOverwrite denied permission for %s", cmd->arg);
+     pr_response_add_err(R_550, _("%s: Overwrite permission denied"), cmd->arg);
+     errno = EACCES;
++    cmd->error_code = EACCES;
+     return PR_ERROR(cmd);
+   }
+ 
+@@ -1267,6 +1271,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
+ 
+       /* Deliberately use EISDIR for anything non-file (e.g. directories). */
+       errno = EISDIR;
++      cmd->error_code = EISDIR;
+       return PR_ERROR(cmd);
+     }
+   }
+@@ -1285,6 +1290,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
+     session.restart_pos = 0L;
+     session.xfer.xfer_type = STOR_DEFAULT;
+     errno = EPERM;
++    cmd->error_code = EPERM;
+     return PR_ERROR(cmd);
+   }
+ 
+@@ -1306,6 +1312,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
+   /* Otherwise everthing is good */
+   if (pr_table_add(cmd->notes, "mod_xfer.store-path",
+       pstrdup(cmd->pool, path), 0) < 0) {
++    cmd->error_code = errno;
+     if (errno != EEXIST) {
+       pr_log_pri(PR_LOG_NOTICE,
+         "notice: error adding 'mod_xfer.store-path': %s", strerror(errno));
+@@ -1326,6 +1333,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
+       pr_response_add_err(R_501,
+         _("REST not compatible with server configuration"));
+       errno = EINVAL;
++      cmd->error_code = EINVAL;
+       return PR_ERROR(cmd);
+     }
+ 
+@@ -1335,6 +1343,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
+       pr_response_add_err(R_550,
+         _("APPE not compatible with server configuration"));
+       errno = EINVAL;
++      cmd->error_code = EINVAL;
+       return PR_ERROR(cmd);
+     }
+ 
+@@ -1400,6 +1409,7 @@ MODRET xfer_pre_stou(cmd_rec *cmd) {
+   tmpfd = mkstemp(filename);
+   if (tmpfd < 0) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     pr_log_pri(PR_LOG_WARNING, "error: unable to use mkstemp(): %s",
+       strerror(xerrno));
+@@ -1428,6 +1438,7 @@ MODRET xfer_pre_stou(cmd_rec *cmd) {
+   if (!filename ||
+       !dir_check(cmd->tmp_pool, cmd, cmd->group, filename, NULL)) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     /* Do not forget to delete the file created by mkstemp(3) if there is
+      * an error.
+@@ -1461,12 +1472,14 @@ MODRET xfer_pre_stou(cmd_rec *cmd) {
+ 
+     /* Deliberately use EISDIR for anything non-file (e.g. directories). */
+     errno = EISDIR;
++    cmd->error_code = errno;
+     return PR_ERROR(cmd);
+   }
+ 
+   /* Otherwise everthing is good */
+   if (pr_table_add(cmd->notes, "mod_xfer.store-path",
+       pstrdup(cmd->pool, filename), 0) < 0) {
++    cmd->error_code = errno;
+     if (errno != EEXIST) {
+       pr_log_pri(PR_LOG_NOTICE,
+         "notice: error adding 'mod_xfer.store-path': %s", strerror(errno));
+@@ -1492,6 +1505,7 @@ MODRET xfer_post_stou(cmd_rec *cmd) {
+   mode_t mode = 0666;
+ 
+   if (pr_fsio_chmod(cmd->arg, mode) < 0) {
++    cmd->error_code = errno;
+ 
+     /* Not much to do but log the error. */
+     pr_log_pri(PR_LOG_NOTICE, "error: unable to chmod '%s' to %04o: %s",
+@@ -1510,6 +1524,7 @@ MODRET xfer_pre_appe(cmd_rec *cmd) {
+   if (xfer_check_limit(cmd) < 0) {
+     pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
+     errno = EPERM;
++    cmd->error_code = EPERM;
+     return PR_ERROR(cmd);
+   }
+ 
+@@ -1580,6 +1595,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
+ 
+     if (stor_fh) {
+       if (pr_fsio_lseek(stor_fh, 0, SEEK_END) == (off_t) -1) {
++        cmd->error_code = errno;
+         pr_log_debug(DEBUG4, "unable to seek to end of '%s' for appending: %s",
+           cmd->arg, strerror(errno));
+         (void) pr_fsio_close(stor_fh);
+@@ -1588,6 +1604,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
+ 
+     } else {
+       ferrno = errno;
++      cmd->error_code = errno;
+ 
+       (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
+         "error opening '%s': %s", cmd->argv[0], session.user,
+@@ -1601,6 +1618,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
+         O_WRONLY|(session.restart_pos ? 0 : O_TRUNC|O_CREAT));
+     if (stor_fh == NULL) {
+       ferrno = errno;
++      cmd->error_code = errno;
+ 
+       (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
+         "error opening '%s': %s", cmd->argv[0], session.user,
+@@ -1614,11 +1632,13 @@ MODRET xfer_stor(cmd_rec *cmd) {
+     int xerrno = 0;
+ 
+     if (pr_fsio_lseek(stor_fh, session.restart_pos, SEEK_SET) == -1) {
++      cmd->error_code = errno;
+       pr_log_debug(DEBUG4, "unable to seek to position %" PR_LU " of '%s': %s",
+         (pr_off_t) session.restart_pos, cmd->arg, strerror(errno));
+       xerrno = errno;
+ 
+     } else if (pr_fsio_stat(path, &st) == -1) {
++      cmd->error_code = errno;
+       pr_log_debug(DEBUG4, "unable to stat '%s': %s", cmd->arg,
+         strerror(errno));
+       xerrno = errno;
+@@ -1755,6 +1775,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
+       pr_data_abort(EPERM, FALSE);
+       errno = EPERM;
+ #endif
++      cmd->error_code = errno;
+       return PR_ERROR(cmd);
+     }
+ 
+@@ -1766,6 +1787,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
+     res = pr_fsio_write(stor_fh, lbuf, len);
+     if (res != len) {
+       int xerrno = EIO;
++      cmd->error_code = errno;
+ 
+       if (res < 0)
+         xerrno = errno;
+@@ -1795,6 +1817,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
+ 
+     /* default abort errno, in case session.d et al has already gone away */
+     int xerrno = ECONNABORTED;
++    cmd->error_code = ECONNABORTED;
+ 
+     stor_abort();
+ 
+@@ -1814,6 +1837,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
+ 
+     if (stor_complete() < 0) {
+       int xerrno = errno;
++      cmd->error_code = errno;
+ 
+       _log_transfer('i', 'i');
+ 
+@@ -1826,12 +1850,14 @@ MODRET xfer_stor(cmd_rec *cmd) {
+       if (xerrno == EDQUOT) {
+         pr_response_add_err(R_552, "%s: %s", cmd->arg, strerror(xerrno));
+         errno = xerrno;
++        cmd->error_code = errno;
+         return PR_ERROR(cmd);
+       }
+ #elif defined(EFBIG)
+       if (xerrno == EFBIG) {
+         pr_response_add_err(R_552, "%s: %s", cmd->arg, strerror(xerrno));
+         errno = xerrno;
++        cmd->error_code = errno;
+         return PR_ERROR(cmd);
+       }
+ #endif
+@@ -1845,6 +1871,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
+         session.xfer.path_hidden) {
+       if (pr_fsio_rename(session.xfer.path_hidden, session.xfer.path) != 0) {
+         int xerrno = errno;
++        cmd->error_code = errno;
+ 
+         /* This should only fail on a race condition with a chmod/chown
+          * or if STOR_APPEND is on and the permissions are squirrely.
+@@ -1947,6 +1974,7 @@ MODRET xfer_pre_retr(cmd_rec *cmd) {
+     pr_response_add_err(R_500, _("'%s' not understood"),
+       pr_cmd_get_displayable_str(cmd, NULL));
+     errno = EINVAL;
++    cmd->error_code = EINVAL;
+     return PR_ERROR(cmd);
+   }
+ 
+@@ -1956,6 +1984,7 @@ MODRET xfer_pre_retr(cmd_rec *cmd) {
+   if (!dir ||
+       !dir_check(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
+     int xerrno = errno;
++    cmd->error_code;
+ 
+     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
+ 
+@@ -1978,12 +2007,14 @@ MODRET xfer_pre_retr(cmd_rec *cmd) {
+   if (xfer_check_limit(cmd) < 0) {
+     pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
+     errno = EPERM;
++    cmd->error_code = EPERM;
+     return PR_ERROR(cmd);
+   }
+ 
+   fmode = file_mode(dir);
+   if (fmode == 0) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
+ 
+@@ -2000,6 +2031,7 @@ MODRET xfer_pre_retr(cmd_rec *cmd) {
+ 
+     /* Deliberately use EISDIR for anything non-file (e.g. directories). */
+     errno = EISDIR;
++    cmd->error_code = EISDIR;
+     return PR_ERROR(cmd);
+   }
+ 
+@@ -2014,12 +2046,14 @@ MODRET xfer_pre_retr(cmd_rec *cmd) {
+       cmd->arg);
+     session.restart_pos = 0L;
+     errno = EPERM;
++    cmd->error_code = EPERM;
+     return PR_ERROR(cmd);
+   }
+ 
+   /* Otherwise everthing is good */
+   if (pr_table_add(cmd->notes, "mod_xfer.retr-path",
+       pstrdup(cmd->pool, dir), 0) < 0) {
++    cmd->error_code = errno;
+     if (errno != EEXIST) {
+       pr_log_pri(PR_LOG_NOTICE, "notice: error adding 'mod_xfer.retr-path': %s",
+         strerror(errno));
+@@ -2046,6 +2080,7 @@ MODRET xfer_retr(cmd_rec *cmd) {
+   retr_fh = pr_fsio_open(dir, O_RDONLY);
+   if (retr_fh == NULL) {
+     int xerrno = errno;
++    cmd->error_code = errno;
+ 
+     (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
+       "error opening '%s': %s", cmd->argv[0], session.user,
+@@ -2059,6 +2094,7 @@ MODRET xfer_retr(cmd_rec *cmd) {
+   if (pr_fsio_stat(dir, &st) < 0) {
+     /* Error stat'ing the file. */
+     int xerrno = errno;
++    cmd->error_code = errno;
+     pr_fsio_close(retr_fh);
+     errno = xerrno;
+ 
+@@ -2083,6 +2119,7 @@ MODRET xfer_retr(cmd_rec *cmd) {
+     if (pr_fsio_lseek(retr_fh, session.restart_pos,
+         SEEK_SET) == (off_t) -1) {
+       int xerrno = errno;
++      cmd->error_code = errno;
+       pr_fsio_close(retr_fh);
+       errno = xerrno;
+       retr_fh = NULL;
+@@ -2143,6 +2180,7 @@ MODRET xfer_retr(cmd_rec *cmd) {
+     retr_abort();
+ 
+     /* Set errno to EPERM ("Operation not permitted") */
++    cmd->error_code = EPERM;
+     pr_data_abort(EPERM, FALSE);
+     return PR_ERROR(cmd);
+   }
+@@ -2174,6 +2212,7 @@ MODRET xfer_retr(cmd_rec *cmd) {
+        * is preserved; errno itself might be overwritten in retr_abort().
+        */
+       int xerrno = errno;
++      cmd->error_code = errno;
+ 
+       retr_abort();
+       pr_data_abort(xerrno, FALSE);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/006.18845170.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,20 @@
+http://bugs.proftpd.org/show_bug.cgi?id=4069
+https://github.com/proftpd/proftpd/commit/38cf9e5028cbddee2b0dcba436d60199da1edf80
+
+--- a/modules/mod_ls.c
++++ b/modules/mod_ls.c
+@@ -2755,6 +2755,13 @@ MODRET ls_nlst(cmd_rec *cmd) {
+     parse_list_opts(&list_options, &glob_flags, TRUE);
+   }
+ 
++  /* If, after parsing out any options, the target string is empty, assume
++   * the current directory (Bug#4069).
++   */
++  if (*target == '\0') {
++    target = pstrdup(cmd->tmp_pool, ".");
++  }
++
+   /* If the target starts with '~' ... */
+   if (*target == '~') {
+     char pb[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/007.19575647.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,53 @@
+diff --git a/include/pr-syslog.h b/include/pr-syslog.h
+index 3eea2be..774a3b0 100644
+--- a/include/pr-syslog.h
++++ b/include/pr-syslog.h
+@@ -68,7 +68,7 @@
+ #elif defined(__hpux)
+ # define PR_PATH_LOG	"/dev/log.un"
+ #else
+-# define PR_PATH_LOG	"/dev/log"
++# define PR_PATH_LOG	"/dev/conslog"
+ #endif
+ 
+ /* Close desriptor used to write to system logger. */
+diff --git a/lib/pr-syslog.c b/lib/pr-syslog.c
+index 3a62956..2b3d747 100644
+--- a/lib/pr-syslog.c
++++ b/lib/pr-syslog.c
+@@ -265,7 +265,7 @@ static void pr_vsyslog(int sockfd, int pri, register const char *fmt,
+   send(sockfd, logbuf, buflen, 0);
+ #else
+ 
+-  /* Prepare the structs for use by putmsg(). As /dev/log is a STREAMS
++  /* Prepare the structs for use by putmsg(). As /dev/conslog is a STREAMS
+    * device on Solaris (and possibly other platforms?), putmsg() is
+    * used so that syslog facility and level are properly honored; write()
+    * does not seem to work as desired.
+@@ -347,22 +347,11 @@ int pr_openlog(const char *ident, int opts, int facility) {
+   }
+ #else
+   sockfd = open(PR_PATH_LOG, O_WRONLY);
+-
+-# ifdef SOLARIS2
+-  /* Workaround for a /dev/log bug (SunSolve bug #4817079) on Solaris. */
+-  if (sockfd >= 0) {
+-    struct strioctl ic;
+-
+-    ic.ic_cmd = I_ERRLOG;
+-    ic.ic_timout = 0;
+-    ic.ic_len = 0;
+-    ic.ic_dp = NULL;
+-
+-    if (ioctl(sockfd, I_STR, &ic) < 0)
+-      fprintf(stderr, "error setting I_ERRLOG on " PR_PATH_LOG ": %s\n",
+-        strerror(errno));
++  if (sockfd < 0) {
++    fprintf(stderr, "failed to open %s: %d, %s\n",
++      PR_PATH_LOG, errno, strerror(errno));
++    exit(EXIT_FAILURE);
+   }
+-# endif /* SOLARIS2 */
+ #endif
+ 
+   return sockfd;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/008.19596338.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,157 @@
+diff --git a/include/glibc-glob.h b/include/glibc-glob.h
+index 422089a..a55899a 100644
+--- a/include/glibc-glob.h
++++ b/include/glibc-glob.h
+@@ -85,6 +85,7 @@ extern "C" {
+ #define	GLOB_ABORTED	2	/* Read error.  */
+ #define	GLOB_NOMATCH	3	/* No matches found.  */
+ #define GLOB_NOSYS	4	/* Not implemented.  */
++#define GLOB_LIMIT	5       /* MAX_RESULTS limit reached */
+ #ifdef _GNU_SOURCE
+ /* Previous versions of this file defined GLOB_ABEND instead of
+    GLOB_ABORTED.  Provide a compatibility definition here.  */
+diff --git a/lib/glibc-glob.c b/lib/glibc-glob.c
+index 43b02bc..92c2b94 100644
+--- a/lib/glibc-glob.c
++++ b/lib/glibc-glob.c
+@@ -40,6 +40,15 @@ char *alloca ();
+ #define MAX_RECURSION 	PR_TUNABLE_GLOBBING_MAX_RECURSION
+ #define MAX_RESULTS	PR_TUNABLE_GLOBBING_MAX_MATCHES
+ 
++unsigned long GlobMaxResults = 0;
++
++static unsigned long get_globmaxresults(void)
++{
++  if (GlobMaxResults > 0)
++    return GlobMaxResults;
++  return MAX_RESULTS;
++}
++
+ /* Enable GNU extensions in glob.h.  */
+ #ifndef _GNU_SOURCE
+ # define _GNU_SOURCE	1
+@@ -239,8 +248,6 @@ extern void bcopy ();
+ # define mempcpy(Dest, Src, Len) __mempcpy (Dest, Src, Len)
+ #endif
+ 
+-static unsigned long nbresults;
+-
+ #ifndef	__GNU_LIBRARY__
+ # ifdef	__GNUC__
+ __inline
+@@ -1157,7 +1164,6 @@ int
+ glob (const char *pattern, int flags, int (*errfunc) __P((const char *, int)),
+     glob_t *pglob)
+ {
+-	nbresults = 0UL;
+ 	return glob_limited(0U, pattern, flags, errfunc, pglob);
+ }
+ 
+@@ -1318,6 +1324,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
+   size_t nfound;
+   int meta;
+   int save;
++  int ret_glob_limit = 0;
+ 
+   meta = __glob_pattern_p (pattern, !(flags & GLOB_NOESCAPE));
+   if (meta == 0 && (flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
+@@ -1428,10 +1435,6 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
+ #endif
+ 		  if (d == NULL)
+ 		    break;
+-		  if (nbresults > MAX_RESULTS) {	
+-			  break;
+-		  }
+-		  nbresults++;	
+ 		  if (! REAL_DIR_ENTRY (d))
+ 		    continue;
+ 
+@@ -1463,6 +1466,14 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
+ 		      new->next = names;
+ 		      names = new;
+ 		      ++nfound;
++
++		      if (nfound > get_globmaxresults()) {
++                        for (; names != NULL; names = names->next)
++                          free(names->name);
++                        nfound = 0;
++		        ret_glob_limit = 1;
++		        break;
++		      }
+ 		    }
+ 		}
+ 	    }
+@@ -1516,6 +1527,9 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
+     }
+   __set_errno (save);
+ 
++  if (ret_glob_limit)
++    return GLOB_LIMIT;
++
+   return nfound == 0 ? GLOB_NOMATCH : 0;
+ 
+  memory_error:
+diff --git a/modules/mod_core.c b/modules/mod_core.c
+index e33ee11..1f6da19 100644
+--- a/modules/mod_core.c
++++ b/modules/mod_core.c
+@@ -973,6 +973,26 @@ MODRET set_maxconnrate(cmd_rec *cmd) {
+   return PR_HANDLED(cmd);
+ }
+ 
++extern unsigned long GlobMaxResults;
++
++MODRET set_globmaxresults(cmd_rec *cmd) {
++  unsigned long max;
++  char *endp;
++
++  CHECK_ARGS(cmd,1);
++  CHECK_CONF(cmd,CONF_ROOT);
++
++  max = strtoul(cmd->argv[1], &endp, 10);
++
++  if ((endp && *endp) || max == 0)
++    CONF_ERROR(cmd, "argument must be greater than 0");
++
++  pr_log_debug(DEBUG1, "setting GlobMaxResults to %lu", max);
++
++  GlobMaxResults = max;
++  return PR_HANDLED(cmd);
++}
++
+ MODRET set_timeoutidle(cmd_rec *cmd) {
+   int timeout = -1;
+   config_rec *c = NULL;
+@@ -6108,6 +6128,7 @@ static conftable core_conftab[] = {
+   { "DisplayConnect",		set_displayconnect,		NULL },
+   { "DisplayQuit",		set_displayquit,		NULL },
+   { "From",			add_from,			NULL },
++  { "GlobMaxResults",		set_globmaxresults,	        NULL },
+   { "Group",			set_group, 			NULL },
+   { "GroupOwner",		add_groupowner,			NULL },
+   { "HideFiles",		set_hidefiles,			NULL },
+diff --git a/modules/mod_ls.c b/modules/mod_ls.c
+index 5b95d2b..e8bbb74 100644
+--- a/modules/mod_ls.c
++++ b/modules/mod_ls.c
+@@ -2015,6 +2015,8 @@ static int dolist(cmd_rec *cmd, const char *opt, int clearflags) {
+         pr_response_add(R_226, _("Read error during globbing of %s"),
+           pr_fs_encode_path(cmd->tmp_pool, arg));
+ 
++      } else if (a == GLOB_LIMIT) {
++        pr_response_add(R_226, _("Too many files"));
+       } else if (a != GLOB_NOMATCH) {
+         pr_response_add(R_226, _("Unknown error during globbing of %s"),
+           pr_fs_encode_path(cmd->tmp_pool, arg));
+@@ -2837,7 +2839,10 @@ MODRET ls_nlst(cmd_rec *cmd) {
+           return PR_HANDLED(cmd);
+         }
+ 
+-        pr_response_add_err(R_450, _("No files found"));
++        if (res == GLOB_LIMIT)
++          pr_response_add_err(R_451, _("Too many files"));
++        else
++          pr_response_add_err(R_450, _("No files found"));
+         return PR_ERROR(cmd);
+       }
+     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/009.19668629.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,18 @@
+http://bugs.proftpd.org/show_bug.cgi?id=4109
+https://github.com/proftpd/proftpd/commit/0b8afb267eb6fd6acf98595a8c2812cd27ac11a0
+
+diff --git a/src/inet.c b/src/inet.c
+index f5602e0..d0a0631 100644
+--- a/src/inet.c
++++ b/src/inet.c
+@@ -770,8 +770,9 @@ int pr_inet_set_proto_opts(pool *p, conn_t *c, int mss, int nodelay,
+   if (pr_netaddr_use_ipv6()) {
+     /* Only set TCLASS flags on IPv6 sockets; IPv4 sockets use TOS. */
+     if (pr_netaddr_get_family(c->local_addr) == AF_INET6) {
++      int level = ipv6_proto;
+       if (c->listen_fd != -1) {
+-        if (setsockopt(c->listen_fd, ip_level, IPV6_TCLASS, (void *) &tos,
++        if (setsockopt(c->listen_fd, level, IPV6_TCLASS, (void *) &tos,
+             sizeof(tos)) < 0) {
+           pr_log_pri(PR_LOG_NOTICE, "error setting listen fd IPV6_TCLASS: %s",
+             strerror(errno));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/010.20393612.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,15 @@
+http://bugs.proftpd.org/show_bug.cgi?id=4132
+
+diff --git a/src/log.c b/src/log.c
+index 4b83413..c83b8ee 100644
+--- a/src/log.c
++++ b/src/log.c
+@@ -36,7 +36,7 @@
+ static int syslog_open = FALSE;
+ static int syslog_discard = FALSE;
+ static int logstderr = TRUE;
+-static int debug_level = DEBUG0;	/* Default is no debug logging */
++static int debug_level = DEBUG0 - 1;	/* Default is no debug logging */
+ static int facility = LOG_DAEMON;
+ static int set_facility = -1;
+ static char systemlog_fn[PR_TUNABLE_PATH_MAX] = {'\0'};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/011.19693019-proftpd-slow_ls.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,103 @@
+The fix was developed in-house and submitted upstream. The upstream bug
+is:
+
+http://bugs.proftpd.org/show_bug.cgi?id=4157
+
+diff --git a/modules/mod_core.c b/modules/mod_core.c
+--- a/modules/mod_core.c
++++ b/modules/mod_core.c
+@@ -40,6 +40,7 @@
+ /* From src/main.c */
+ extern unsigned long max_connects;
+ extern unsigned int max_connect_interval;
++extern unsigned char tracing_enabled;
+ 
+ /* From modules/mod_site.c */
+ extern modret_t *site_dispatch(cmd_rec*);
+@@ -1381,6 +1382,8 @@
+   int per_session = FALSE;
+   unsigned int idx = 1;
+ 
++  tracing_enabled = TRUE;
++
+   if (cmd->argc-1 < 1)
+     CONF_ERROR(cmd, "wrong number of parameters");
+   CHECK_CONF(cmd, CONF_ROOT);
+diff --git a/modules/mod_ls.c b/modules/mod_ls.c
+--- a/modules/mod_ls.c
++++ b/modules/mod_ls.c
+@@ -307,10 +307,12 @@
+ 
+ static int sendline(int flags, char *fmt, ...) {
+   va_list msg;
+-  char buf[PR_TUNABLE_BUFFER_SIZE+1] = {'\0'};
++  char buf[PR_TUNABLE_BUFFER_SIZE+1];
+   int res = 0;
+   size_t buflen, listbuflen;
+ 
++  (void) memset(buf, '\0', sizeof(buf));
++
+   if (listbuf == NULL) {
+     listbufsz = pr_config_get_server_xfer_bufsz(PR_NETIO_IO_WR);
+     listbuf = listbuf_ptr = pcalloc(session.pool, listbufsz);
+diff --git a/src/fsio.c b/src/fsio.c
+--- a/src/fsio.c
++++ b/src/fsio.c
+@@ -556,10 +556,12 @@
+ static int cache_stat(pr_fs_t *fs, const char *path, struct stat *sbuf,
+     unsigned int op) {
+   int res = -1;
+-  char pathbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
++  char pathbuf[PR_TUNABLE_PATH_MAX + 1];
+   int (*mystat)(pr_fs_t *, const char *, struct stat *) = NULL;
+   size_t pathlen;
+ 
++  (void) memset(pathbuf, '\0', sizeof(pathbuf));
++
+   /* Sanity checks */
+   if (fs == NULL) {
+     errno = EINVAL;
+@@ -641,8 +643,8 @@
+  * during the hit.
+  */
+ static pr_fs_t *lookup_dir_fs(const char *path, int op) {
+-  char buf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
+-  char tmp_path[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
++  char buf[PR_TUNABLE_PATH_MAX + 1];
++  char tmp_path[PR_TUNABLE_PATH_MAX + 1];
+   pr_fs_t *fs = NULL;
+   int exact = FALSE;
+   size_t tmp_pathlen = 0;
+@@ -651,6 +653,9 @@
+   pr_fs_match_t *fsm = NULL;
+ #endif /* PR_FS_MATCH */
+ 
++  (void) memset(buf, '\0', sizeof(buf));
++  (void) memset(tmp_path, '\0', sizeof(tmp_path));
++
+   sstrncpy(buf, path, sizeof(buf));
+ 
+   /* Check if the given path is an absolute path.  Since there may be
+diff --git a/src/trace.c b/src/trace.c
+--- a/src/trace.c
++++ b/src/trace.c
+@@ -32,6 +32,8 @@
+ 
+ #ifdef PR_USE_TRACE
+ 
++unsigned char tracing_enabled = FALSE;
++
+ static int trace_logfd = -1;
+ static unsigned long trace_opts = PR_TRACE_OPT_DEFAULT;
+ static pool *trace_pool = NULL;
+@@ -473,6 +475,10 @@
+   int res;
+   va_list msg;
+ 
++  /* Optimization: do not run tracing code unless explicitly enabled. */
++  if (tracing_enabled == FALSE)
++    return 0;
++
+   va_start(msg, fmt);
+   res = pr_trace_vmsg(channel, level, fmt, msg);
+   va_end(msg);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/012.20553228-dtrace-provider.patch	Wed Mar 11 03:50:20 2015 -0700
@@ -0,0 +1,66 @@
+http://bugs.proftpd.org/show_bug.cgi?id=4162
+
+--- a/modules/mod_xfer.c	2015-02-03 20:54:22.857151750 -0800
++++ b/modules/mod_xfer.c	2015-02-03 21:10:36.020000070 -0800
+@@ -31,6 +31,9 @@
+ 
+ #include "conf.h"
+ #include "privs.h"
++#ifdef _SOLARIS_DTRACE
++#include "ftp_provider_impl.h"
++#endif
+ 
+ #ifdef HAVE_SYS_SENDFILE_H
+ # include <sys/sendfile.h>
+@@ -1784,7 +1787,24 @@
+      * be doing short writes, and we ideally should be more resilient/graceful
+      * in the face of such things.
+      */
++#ifdef _SOLARIS_DTRACE
++    if (ftp_transfer_start_enabled()) {
++      ftpproto_t proto;
++      FTP_TRANSFER_PROTO(&proto, stor_fh, 0);
++      ftp_transfer_start(&proto);
++    }
++#endif
++
+     res = pr_fsio_write(stor_fh, lbuf, len);
++
++#ifdef _SOLARIS_DTRACE
++    if (ftp_transfer_done_enabled() && res == len) {
++      ftpproto_t proto;
++      FTP_TRANSFER_PROTO(&proto, stor_fh, res);
++      ftp_transfer_done(&proto);
++    }
++#endif
++
+     if (res != len) {
+       int xerrno = EIO;
+       cmd->error_code = errno;
+@@ -2203,7 +2223,24 @@
+     if (XFER_ABORTED)
+       break;
+ 
++#ifdef _SOLARIS_DTRACE
++    if (ftp_transfer_start_enabled()) {
++      ftpproto_t proto;
++      FTP_TRANSFER_PROTO(&proto, retr_fh, 0);
++      ftp_transfer_start(&proto);
++    }
++#endif
++
+     len = transmit_data(nbytes_sent, &curr_pos, lbuf, bufsz);
++
++#ifdef _SOLARIS_DTRACE
++    if (ftp_transfer_done_enabled() && len > 0) {
++      ftpproto_t proto;
++      FTP_TRANSFER_PROTO(&proto, retr_fh, len);
++      ftp_transfer_done(&proto);
++    }
++#endif
++
+     if (len == 0)
+       break;
+ 
+ 
+
--- a/components/proftpd/patches/18845170.patch	Tue Mar 10 11:12:15 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-http://bugs.proftpd.org/show_bug.cgi?id=4069
-https://github.com/proftpd/proftpd/commit/38cf9e5028cbddee2b0dcba436d60199da1edf80
-
---- a/modules/mod_ls.c
-+++ b/modules/mod_ls.c
-@@ -2755,6 +2755,13 @@ MODRET ls_nlst(cmd_rec *cmd) {
-     parse_list_opts(&list_options, &glob_flags, TRUE);
-   }
- 
-+  /* If, after parsing out any options, the target string is empty, assume
-+   * the current directory (Bug#4069).
-+   */
-+  if (*target == '\0') {
-+    target = pstrdup(cmd->tmp_pool, ".");
-+  }
-+
-   /* If the target starts with '~' ... */
-   if (*target == '~') {
-     char pb[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
-
--- a/components/proftpd/patches/19575647.patch	Tue Mar 10 11:12:15 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-diff --git a/include/pr-syslog.h b/include/pr-syslog.h
-index 3eea2be..774a3b0 100644
---- a/include/pr-syslog.h
-+++ b/include/pr-syslog.h
-@@ -68,7 +68,7 @@
- #elif defined(__hpux)
- # define PR_PATH_LOG	"/dev/log.un"
- #else
--# define PR_PATH_LOG	"/dev/log"
-+# define PR_PATH_LOG	"/dev/conslog"
- #endif
- 
- /* Close desriptor used to write to system logger. */
-diff --git a/lib/pr-syslog.c b/lib/pr-syslog.c
-index 3a62956..2b3d747 100644
---- a/lib/pr-syslog.c
-+++ b/lib/pr-syslog.c
-@@ -265,7 +265,7 @@ static void pr_vsyslog(int sockfd, int pri, register const char *fmt,
-   send(sockfd, logbuf, buflen, 0);
- #else
- 
--  /* Prepare the structs for use by putmsg(). As /dev/log is a STREAMS
-+  /* Prepare the structs for use by putmsg(). As /dev/conslog is a STREAMS
-    * device on Solaris (and possibly other platforms?), putmsg() is
-    * used so that syslog facility and level are properly honored; write()
-    * does not seem to work as desired.
-@@ -347,22 +347,11 @@ int pr_openlog(const char *ident, int opts, int facility) {
-   }
- #else
-   sockfd = open(PR_PATH_LOG, O_WRONLY);
--
--# ifdef SOLARIS2
--  /* Workaround for a /dev/log bug (SunSolve bug #4817079) on Solaris. */
--  if (sockfd >= 0) {
--    struct strioctl ic;
--
--    ic.ic_cmd = I_ERRLOG;
--    ic.ic_timout = 0;
--    ic.ic_len = 0;
--    ic.ic_dp = NULL;
--
--    if (ioctl(sockfd, I_STR, &ic) < 0)
--      fprintf(stderr, "error setting I_ERRLOG on " PR_PATH_LOG ": %s\n",
--        strerror(errno));
-+  if (sockfd < 0) {
-+    fprintf(stderr, "failed to open %s: %d, %s\n",
-+      PR_PATH_LOG, errno, strerror(errno));
-+    exit(EXIT_FAILURE);
-   }
--# endif /* SOLARIS2 */
- #endif
- 
-   return sockfd;
--- a/components/proftpd/patches/19596338.patch	Tue Mar 10 11:12:15 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,157 +0,0 @@
-diff --git a/include/glibc-glob.h b/include/glibc-glob.h
-index 422089a..a55899a 100644
---- a/include/glibc-glob.h
-+++ b/include/glibc-glob.h
-@@ -85,6 +85,7 @@ extern "C" {
- #define	GLOB_ABORTED	2	/* Read error.  */
- #define	GLOB_NOMATCH	3	/* No matches found.  */
- #define GLOB_NOSYS	4	/* Not implemented.  */
-+#define GLOB_LIMIT	5       /* MAX_RESULTS limit reached */
- #ifdef _GNU_SOURCE
- /* Previous versions of this file defined GLOB_ABEND instead of
-    GLOB_ABORTED.  Provide a compatibility definition here.  */
-diff --git a/lib/glibc-glob.c b/lib/glibc-glob.c
-index 43b02bc..92c2b94 100644
---- a/lib/glibc-glob.c
-+++ b/lib/glibc-glob.c
-@@ -40,6 +40,15 @@ char *alloca ();
- #define MAX_RECURSION 	PR_TUNABLE_GLOBBING_MAX_RECURSION
- #define MAX_RESULTS	PR_TUNABLE_GLOBBING_MAX_MATCHES
- 
-+unsigned long GlobMaxResults = 0;
-+
-+static unsigned long get_globmaxresults(void)
-+{
-+  if (GlobMaxResults > 0)
-+    return GlobMaxResults;
-+  return MAX_RESULTS;
-+}
-+
- /* Enable GNU extensions in glob.h.  */
- #ifndef _GNU_SOURCE
- # define _GNU_SOURCE	1
-@@ -239,8 +248,6 @@ extern void bcopy ();
- # define mempcpy(Dest, Src, Len) __mempcpy (Dest, Src, Len)
- #endif
- 
--static unsigned long nbresults;
--
- #ifndef	__GNU_LIBRARY__
- # ifdef	__GNUC__
- __inline
-@@ -1157,7 +1164,6 @@ int
- glob (const char *pattern, int flags, int (*errfunc) __P((const char *, int)),
-     glob_t *pglob)
- {
--	nbresults = 0UL;
- 	return glob_limited(0U, pattern, flags, errfunc, pglob);
- }
- 
-@@ -1318,6 +1324,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
-   size_t nfound;
-   int meta;
-   int save;
-+  int ret_glob_limit = 0;
- 
-   meta = __glob_pattern_p (pattern, !(flags & GLOB_NOESCAPE));
-   if (meta == 0 && (flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
-@@ -1428,10 +1435,6 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
- #endif
- 		  if (d == NULL)
- 		    break;
--		  if (nbresults > MAX_RESULTS) {	
--			  break;
--		  }
--		  nbresults++;	
- 		  if (! REAL_DIR_ENTRY (d))
- 		    continue;
- 
-@@ -1463,6 +1466,14 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
- 		      new->next = names;
- 		      names = new;
- 		      ++nfound;
-+
-+		      if (nfound > get_globmaxresults()) {
-+                        for (; names != NULL; names = names->next)
-+                          free(names->name);
-+                        nfound = 0;
-+		        ret_glob_limit = 1;
-+		        break;
-+		      }
- 		    }
- 		}
- 	    }
-@@ -1516,6 +1527,9 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
-     }
-   __set_errno (save);
- 
-+  if (ret_glob_limit)
-+    return GLOB_LIMIT;
-+
-   return nfound == 0 ? GLOB_NOMATCH : 0;
- 
-  memory_error:
-diff --git a/modules/mod_core.c b/modules/mod_core.c
-index e33ee11..1f6da19 100644
---- a/modules/mod_core.c
-+++ b/modules/mod_core.c
-@@ -973,6 +973,26 @@ MODRET set_maxconnrate(cmd_rec *cmd) {
-   return PR_HANDLED(cmd);
- }
- 
-+extern unsigned long GlobMaxResults;
-+
-+MODRET set_globmaxresults(cmd_rec *cmd) {
-+  unsigned long max;
-+  char *endp;
-+
-+  CHECK_ARGS(cmd,1);
-+  CHECK_CONF(cmd,CONF_ROOT);
-+
-+  max = strtoul(cmd->argv[1], &endp, 10);
-+
-+  if ((endp && *endp) || max == 0)
-+    CONF_ERROR(cmd, "argument must be greater than 0");
-+
-+  pr_log_debug(DEBUG1, "setting GlobMaxResults to %lu", max);
-+
-+  GlobMaxResults = max;
-+  return PR_HANDLED(cmd);
-+}
-+
- MODRET set_timeoutidle(cmd_rec *cmd) {
-   int timeout = -1;
-   config_rec *c = NULL;
-@@ -6108,6 +6128,7 @@ static conftable core_conftab[] = {
-   { "DisplayConnect",		set_displayconnect,		NULL },
-   { "DisplayQuit",		set_displayquit,		NULL },
-   { "From",			add_from,			NULL },
-+  { "GlobMaxResults",		set_globmaxresults,	        NULL },
-   { "Group",			set_group, 			NULL },
-   { "GroupOwner",		add_groupowner,			NULL },
-   { "HideFiles",		set_hidefiles,			NULL },
-diff --git a/modules/mod_ls.c b/modules/mod_ls.c
-index 5b95d2b..e8bbb74 100644
---- a/modules/mod_ls.c
-+++ b/modules/mod_ls.c
-@@ -2015,6 +2015,8 @@ static int dolist(cmd_rec *cmd, const char *opt, int clearflags) {
-         pr_response_add(R_226, _("Read error during globbing of %s"),
-           pr_fs_encode_path(cmd->tmp_pool, arg));
- 
-+      } else if (a == GLOB_LIMIT) {
-+        pr_response_add(R_226, _("Too many files"));
-       } else if (a != GLOB_NOMATCH) {
-         pr_response_add(R_226, _("Unknown error during globbing of %s"),
-           pr_fs_encode_path(cmd->tmp_pool, arg));
-@@ -2837,7 +2839,10 @@ MODRET ls_nlst(cmd_rec *cmd) {
-           return PR_HANDLED(cmd);
-         }
- 
--        pr_response_add_err(R_450, _("No files found"));
-+        if (res == GLOB_LIMIT)
-+          pr_response_add_err(R_451, _("Too many files"));
-+        else
-+          pr_response_add_err(R_450, _("No files found"));
-         return PR_ERROR(cmd);
-       }
-     }
--- a/components/proftpd/patches/19668629.patch	Tue Mar 10 11:12:15 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-http://bugs.proftpd.org/show_bug.cgi?id=4109
-https://github.com/proftpd/proftpd/commit/0b8afb267eb6fd6acf98595a8c2812cd27ac11a0
-
-diff --git a/src/inet.c b/src/inet.c
-index f5602e0..d0a0631 100644
---- a/src/inet.c
-+++ b/src/inet.c
-@@ -770,8 +770,9 @@ int pr_inet_set_proto_opts(pool *p, conn_t *c, int mss, int nodelay,
-   if (pr_netaddr_use_ipv6()) {
-     /* Only set TCLASS flags on IPv6 sockets; IPv4 sockets use TOS. */
-     if (pr_netaddr_get_family(c->local_addr) == AF_INET6) {
-+      int level = ipv6_proto;
-       if (c->listen_fd != -1) {
--        if (setsockopt(c->listen_fd, ip_level, IPV6_TCLASS, (void *) &tos,
-+        if (setsockopt(c->listen_fd, level, IPV6_TCLASS, (void *) &tos,
-             sizeof(tos)) < 0) {
-           pr_log_pri(PR_LOG_NOTICE, "error setting listen fd IPV6_TCLASS: %s",
-             strerror(errno));
--- a/components/proftpd/patches/20393612.patch	Tue Mar 10 11:12:15 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-http://bugs.proftpd.org/show_bug.cgi?id=4132
-
-diff --git a/src/log.c b/src/log.c
-index 4b83413..c83b8ee 100644
---- a/src/log.c
-+++ b/src/log.c
-@@ -36,7 +36,7 @@
- static int syslog_open = FALSE;
- static int syslog_discard = FALSE;
- static int logstderr = TRUE;
--static int debug_level = DEBUG0;	/* Default is no debug logging */
-+static int debug_level = DEBUG0 - 1;	/* Default is no debug logging */
- static int facility = LOG_DAEMON;
- static int set_facility = -1;
- static char systemlog_fn[PR_TUNABLE_PATH_MAX] = {'\0'};
--- a/components/proftpd/patches/mod_auth_unix_getgrp.patch	Tue Mar 10 11:12:15 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
---- old/modules/mod_auth_unix.c	Fri May 25 08:31:35 2012
-+++ new/modules/mod_auth_unix.c	Fri May 25 08:31:35 2012
-@@ -896,6 +896,13 @@
- 
-   return gr ? mod_create_data(cmd, (void *) &gr->gr_gid) : PR_DECLINED(cmd);
- }
-+#ifdef HAVE__GETGRPSBYMEMBER
-+  extern int _getgroupsbymember
-+  (
-+	const char* username, gid_t gid_array[],
-+	int maxgids, int numgids
-+  ); 
-+#endif /* HAVE__GETGRPSBYMEMBER */
- 
- /* cmd->argv[0] = name
-  * cmd->argv[1] = (array_header **) group_ids
-@@ -1065,7 +1072,40 @@
-     }
- 
-     free(ptr);
--#else
-+#else /* !HAVE_GETGRSET */
-+#ifdef HAVE__GETGRPSBYMEMBER
-+    gid_t group_ids[NGROUPS_MAX];
-+    int ngroups = NGROUPS_MAX;
-+    register unsigned int i;
-+
-+    pr_trace_msg("auth", 4,
-+      "using _getgroupsbymember() to look up group membership");
-+
-+    memset(group_ids, 0, sizeof(group_ids));
-+
-+    group_ids[0]=pw->pw_gid;
-+    ngroups=
-+      _getgroupsbymember(pw->pw_name, group_ids, NGROUPS_MAX, 1);
-+
-+    if (ngroups < 0) {
-+	pr_log_pri(PR_LOG_ERR,
-+	    "_getgroupsbymember error: %s", strerror(errno));
-+	return PR_DECLINED(cmd);
-+    }
-+
-+    for (i = 0; i < ngroups; i++) {
-+      gr = my_getgrgid(group_ids[i]);
-+      if (gr) {
-+        if (gids && pw->pw_gid != gr->gr_gid)
-+          *((gid_t *) push_array(gids)) = gr->gr_gid;
-+
-+        if (groups && pw->pw_gid != gr->gr_gid) {
-+          *((char **) push_array(groups)) = pstrdup(session.pool,
-+            gr->gr_name);
-+        }
-+      }
-+    }
-+#else /* !HAVE__GETGRPSBYMEMBER */
-     char **gr_member = NULL;
- 
-     /* This is where things get slow, expensive, and ugly.  Loop through
-@@ -1091,6 +1131,7 @@
-         }
-       }
-     }
-+#endif /* !HAVE__GETGROUPSBYMEMBER */
- #endif /* !HAVE_GETGRSET */
-   }
- 
--- a/components/proftpd/patches/proftpd-configuration-html.patch	Tue Mar 10 11:12:15 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
---- a/doc/Configuration.html
-+++ b/doc/Configuration.html
-@@ -10805,7 +10805,7 @@ CLASS="SYNOPSIS"
- ></DT
- ><DD
- ><P
-->IdentLookups on</P
-+>IdentLookups off</P
- ></DD
- ><DT
- ><PRE
-
--- a/components/proftpd/patches/proftpd-error_code.patch	Tue Mar 10 11:12:15 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,466 +0,0 @@
-diff --git a/include/dirtree.h b/include/dirtree.h
-index 0f41986..fe7b14b 100644
---- a/include/dirtree.h
-+++ b/include/dirtree.h
-@@ -126,6 +126,10 @@ typedef struct cmd_struc {
-   pr_table_t *notes;		/* Private data for passing/retaining between handlers */
- 
-   int cmd_id;			/* Index into commands list, for faster comparisons */
-+
-+  int error_code;               /* Stores errno of failed file transfer
-+                                 * commands. Required for Solaris auditing.
-+                                 */
- } cmd_rec;
- 
- struct config_struc {
-diff --git a/modules/mod_core.c b/modules/mod_core.c
-index e33ee11..f680748 100644
---- a/modules/mod_core.c
-+++ b/modules/mod_core.c
-@@ -4775,6 +4775,7 @@ MODRET core_rmd(cmd_rec *cmd) {
-   dir = dir_canonical_path(cmd->tmp_pool, dir);
-   if (dir == NULL) {
-     int xerrno = EINVAL;
-+    cmd->error_code = EINVAL;
- 
-     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
- 
-@@ -4784,6 +4785,7 @@ MODRET core_rmd(cmd_rec *cmd) {
- 
-   if (!dir_check_canon(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
-     int xerrno = EACCES;
-+    cmd->error_code = EACCES;
- 
-     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
- 
-@@ -4793,6 +4795,7 @@ MODRET core_rmd(cmd_rec *cmd) {
- 
-   if (pr_fsio_rmdir(dir) < 0) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
-       "error removing directory '%s': %s", cmd->argv[0], session.user,
-@@ -4849,6 +4852,7 @@ MODRET core_mkd(cmd_rec *cmd) {
-   dir = dir_canonical_path(cmd->tmp_pool, dir);
-   if (dir == NULL) {
-     int xerrno = EINVAL;
-+    cmd->error_code = EINVAL;
- 
-     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
- 
-@@ -4858,6 +4862,7 @@ MODRET core_mkd(cmd_rec *cmd) {
- 
-   if (!dir_check_canon(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
-     int xerrno = EACCES;
-+    cmd->error_code = EACCES;
- 
-     pr_log_debug(DEBUG8, "%s command denied by <Limit> config", cmd->argv[0]);
-     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
-@@ -4869,6 +4874,7 @@ MODRET core_mkd(cmd_rec *cmd) {
-   if (pr_fsio_smkdir(cmd->tmp_pool, dir, 0777, session.fsuid,
-       session.fsgid) < 0) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
-       "error making directory '%s': %s", cmd->argv[0], session.user,
-@@ -4915,6 +4921,7 @@ MODRET core_mdtm(cmd_rec *cmd) {
-       !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
-       pr_fsio_stat(path, &st) == -1) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
- 
-@@ -5026,6 +5033,7 @@ MODRET core_dele(cmd_rec *cmd) {
-   path = dir_canonical_path(cmd->tmp_pool, path);
-   if (path == NULL) {
-     int xerrno = ENOENT;
-+    cmd->error_code = ENOENT;
- 
-     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
- 
-@@ -5035,6 +5043,7 @@ MODRET core_dele(cmd_rec *cmd) {
- 
-   if (!dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     pr_log_debug(DEBUG7, "deleting '%s' denied by <Limit> configuration", path);
-     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
-@@ -5051,6 +5060,7 @@ MODRET core_dele(cmd_rec *cmd) {
-   pr_fs_clear_cache();
-   if (pr_fsio_lstat(path, &st) < 0) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     pr_log_debug(DEBUG3, "unable to lstat '%s': %s", path, strerror(xerrno));
-     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
-@@ -5065,6 +5075,7 @@ MODRET core_dele(cmd_rec *cmd) {
-    */
-   if (S_ISDIR(st.st_mode)) {
-     int xerrno = EISDIR;
-+    cmd->error_code = EISDIR;
- 
-     (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
-       "error deleting '%s': %s", cmd->argv[0], session.user,
-@@ -5081,6 +5092,7 @@ MODRET core_dele(cmd_rec *cmd) {
-  
-   if (pr_fsio_unlink(path) < 0) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
-       "error deleting '%s': %s", cmd->argv[0], session.user,
-@@ -5165,6 +5177,7 @@ MODRET core_rnto(cmd_rec *cmd) {
-     pr_log_debug(DEBUG6, "AllowOverwrite denied permission for %s", path);
-     pr_response_add_err(R_550, _("%s: Rename permission denied"), cmd->arg);
-     errno = EACCES;
-+    cmd->error_code = EACCES;
-     return PR_ERROR(cmd);
-   }
- 
-@@ -5172,6 +5185,7 @@ MODRET core_rnto(cmd_rec *cmd) {
-       !dir_check_canon(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
-       pr_fsio_rename(session.xfer.path, path) == -1) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     if (xerrno != EXDEV) {
-       (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
-@@ -5223,6 +5237,7 @@ MODRET core_rnto(cmd_rec *cmd) {
-      */
-     if (pr_fs_copy_file(session.xfer.path, path) < 0) {
-       xerrno = errno;
-+      cmd->error_code = errno;
- 
-       (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
-         "error copying '%s' to '%s': %s", cmd->argv[0], session.user,
-@@ -5238,6 +5253,8 @@ MODRET core_rnto(cmd_rec *cmd) {
- 
-     /* Once copied, unlink the original file. */
-     if (pr_fsio_unlink(session.xfer.path) < 0) {
-+      cmd->error_code = errno;
-+
-       pr_log_debug(DEBUG0, "error unlinking '%s': %s", session.xfer.path,
-         strerror(errno));
-     }
-@@ -5295,6 +5312,7 @@ MODRET core_rnfr(cmd_rec *cmd) {
-       !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
-       !exists(path)) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
- 
-diff --git a/modules/mod_xfer.c b/modules/mod_xfer.c
-index bcfb487..41c597e 100644
---- a/modules/mod_xfer.c
-+++ b/modules/mod_xfer.c
-@@ -1190,6 +1190,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
-   if (cmd->argc < 2) {
-     pr_response_add_err(R_500, _("'%s' not understood"),
-       pr_cmd_get_displayable_str(cmd, NULL));
-+    cmd->error_code = EINVAL;
-     errno = EINVAL;
-     return PR_ERROR(cmd);
-   }
-@@ -1200,6 +1201,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
-   if (!path ||
-       !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     pr_log_debug(DEBUG8, "%s %s denied by <Limit> configuration", cmd->argv[0],
-       cmd->arg);
-@@ -1232,6 +1234,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
-   if (xfer_check_limit(cmd) < 0) {
-     pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
-     errno = EPERM;
-+    cmd->error_code = EPERM;
-     return PR_ERROR(cmd);
-   }
- 
-@@ -1244,6 +1247,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
-     pr_log_debug(DEBUG6, "AllowOverwrite denied permission for %s", cmd->arg);
-     pr_response_add_err(R_550, _("%s: Overwrite permission denied"), cmd->arg);
-     errno = EACCES;
-+    cmd->error_code = EACCES;
-     return PR_ERROR(cmd);
-   }
- 
-@@ -1267,6 +1271,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
- 
-       /* Deliberately use EISDIR for anything non-file (e.g. directories). */
-       errno = EISDIR;
-+      cmd->error_code = EISDIR;
-       return PR_ERROR(cmd);
-     }
-   }
-@@ -1285,6 +1290,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
-     session.restart_pos = 0L;
-     session.xfer.xfer_type = STOR_DEFAULT;
-     errno = EPERM;
-+    cmd->error_code = EPERM;
-     return PR_ERROR(cmd);
-   }
- 
-@@ -1306,6 +1312,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
-   /* Otherwise everthing is good */
-   if (pr_table_add(cmd->notes, "mod_xfer.store-path",
-       pstrdup(cmd->pool, path), 0) < 0) {
-+    cmd->error_code = errno;
-     if (errno != EEXIST) {
-       pr_log_pri(PR_LOG_NOTICE,
-         "notice: error adding 'mod_xfer.store-path': %s", strerror(errno));
-@@ -1326,6 +1333,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
-       pr_response_add_err(R_501,
-         _("REST not compatible with server configuration"));
-       errno = EINVAL;
-+      cmd->error_code = EINVAL;
-       return PR_ERROR(cmd);
-     }
- 
-@@ -1335,6 +1343,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
-       pr_response_add_err(R_550,
-         _("APPE not compatible with server configuration"));
-       errno = EINVAL;
-+      cmd->error_code = EINVAL;
-       return PR_ERROR(cmd);
-     }
- 
-@@ -1400,6 +1409,7 @@ MODRET xfer_pre_stou(cmd_rec *cmd) {
-   tmpfd = mkstemp(filename);
-   if (tmpfd < 0) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     pr_log_pri(PR_LOG_WARNING, "error: unable to use mkstemp(): %s",
-       strerror(xerrno));
-@@ -1428,6 +1438,7 @@ MODRET xfer_pre_stou(cmd_rec *cmd) {
-   if (!filename ||
-       !dir_check(cmd->tmp_pool, cmd, cmd->group, filename, NULL)) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     /* Do not forget to delete the file created by mkstemp(3) if there is
-      * an error.
-@@ -1461,12 +1472,14 @@ MODRET xfer_pre_stou(cmd_rec *cmd) {
- 
-     /* Deliberately use EISDIR for anything non-file (e.g. directories). */
-     errno = EISDIR;
-+    cmd->error_code = errno;
-     return PR_ERROR(cmd);
-   }
- 
-   /* Otherwise everthing is good */
-   if (pr_table_add(cmd->notes, "mod_xfer.store-path",
-       pstrdup(cmd->pool, filename), 0) < 0) {
-+    cmd->error_code = errno;
-     if (errno != EEXIST) {
-       pr_log_pri(PR_LOG_NOTICE,
-         "notice: error adding 'mod_xfer.store-path': %s", strerror(errno));
-@@ -1492,6 +1505,7 @@ MODRET xfer_post_stou(cmd_rec *cmd) {
-   mode_t mode = 0666;
- 
-   if (pr_fsio_chmod(cmd->arg, mode) < 0) {
-+    cmd->error_code = errno;
- 
-     /* Not much to do but log the error. */
-     pr_log_pri(PR_LOG_NOTICE, "error: unable to chmod '%s' to %04o: %s",
-@@ -1510,6 +1524,7 @@ MODRET xfer_pre_appe(cmd_rec *cmd) {
-   if (xfer_check_limit(cmd) < 0) {
-     pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
-     errno = EPERM;
-+    cmd->error_code = EPERM;
-     return PR_ERROR(cmd);
-   }
- 
-@@ -1580,6 +1595,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
- 
-     if (stor_fh) {
-       if (pr_fsio_lseek(stor_fh, 0, SEEK_END) == (off_t) -1) {
-+        cmd->error_code = errno;
-         pr_log_debug(DEBUG4, "unable to seek to end of '%s' for appending: %s",
-           cmd->arg, strerror(errno));
-         (void) pr_fsio_close(stor_fh);
-@@ -1588,6 +1604,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
- 
-     } else {
-       ferrno = errno;
-+      cmd->error_code = errno;
- 
-       (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
-         "error opening '%s': %s", cmd->argv[0], session.user,
-@@ -1601,6 +1618,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
-         O_WRONLY|(session.restart_pos ? 0 : O_TRUNC|O_CREAT));
-     if (stor_fh == NULL) {
-       ferrno = errno;
-+      cmd->error_code = errno;
- 
-       (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
-         "error opening '%s': %s", cmd->argv[0], session.user,
-@@ -1614,11 +1632,13 @@ MODRET xfer_stor(cmd_rec *cmd) {
-     int xerrno = 0;
- 
-     if (pr_fsio_lseek(stor_fh, session.restart_pos, SEEK_SET) == -1) {
-+      cmd->error_code = errno;
-       pr_log_debug(DEBUG4, "unable to seek to position %" PR_LU " of '%s': %s",
-         (pr_off_t) session.restart_pos, cmd->arg, strerror(errno));
-       xerrno = errno;
- 
-     } else if (pr_fsio_stat(path, &st) == -1) {
-+      cmd->error_code = errno;
-       pr_log_debug(DEBUG4, "unable to stat '%s': %s", cmd->arg,
-         strerror(errno));
-       xerrno = errno;
-@@ -1755,6 +1775,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
-       pr_data_abort(EPERM, FALSE);
-       errno = EPERM;
- #endif
-+      cmd->error_code = errno;
-       return PR_ERROR(cmd);
-     }
- 
-@@ -1766,6 +1787,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
-     res = pr_fsio_write(stor_fh, lbuf, len);
-     if (res != len) {
-       int xerrno = EIO;
-+      cmd->error_code = errno;
- 
-       if (res < 0)
-         xerrno = errno;
-@@ -1795,6 +1817,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
- 
-     /* default abort errno, in case session.d et al has already gone away */
-     int xerrno = ECONNABORTED;
-+    cmd->error_code = ECONNABORTED;
- 
-     stor_abort();
- 
-@@ -1814,6 +1837,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
- 
-     if (stor_complete() < 0) {
-       int xerrno = errno;
-+      cmd->error_code = errno;
- 
-       _log_transfer('i', 'i');
- 
-@@ -1826,12 +1850,14 @@ MODRET xfer_stor(cmd_rec *cmd) {
-       if (xerrno == EDQUOT) {
-         pr_response_add_err(R_552, "%s: %s", cmd->arg, strerror(xerrno));
-         errno = xerrno;
-+        cmd->error_code = errno;
-         return PR_ERROR(cmd);
-       }
- #elif defined(EFBIG)
-       if (xerrno == EFBIG) {
-         pr_response_add_err(R_552, "%s: %s", cmd->arg, strerror(xerrno));
-         errno = xerrno;
-+        cmd->error_code = errno;
-         return PR_ERROR(cmd);
-       }
- #endif
-@@ -1845,6 +1871,7 @@ MODRET xfer_stor(cmd_rec *cmd) {
-         session.xfer.path_hidden) {
-       if (pr_fsio_rename(session.xfer.path_hidden, session.xfer.path) != 0) {
-         int xerrno = errno;
-+        cmd->error_code = errno;
- 
-         /* This should only fail on a race condition with a chmod/chown
-          * or if STOR_APPEND is on and the permissions are squirrely.
-@@ -1947,6 +1974,7 @@ MODRET xfer_pre_retr(cmd_rec *cmd) {
-     pr_response_add_err(R_500, _("'%s' not understood"),
-       pr_cmd_get_displayable_str(cmd, NULL));
-     errno = EINVAL;
-+    cmd->error_code = EINVAL;
-     return PR_ERROR(cmd);
-   }
- 
-@@ -1956,6 +1984,7 @@ MODRET xfer_pre_retr(cmd_rec *cmd) {
-   if (!dir ||
-       !dir_check(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
-     int xerrno = errno;
-+    cmd->error_code;
- 
-     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
- 
-@@ -1978,12 +2007,14 @@ MODRET xfer_pre_retr(cmd_rec *cmd) {
-   if (xfer_check_limit(cmd) < 0) {
-     pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
-     errno = EPERM;
-+    cmd->error_code = EPERM;
-     return PR_ERROR(cmd);
-   }
- 
-   fmode = file_mode(dir);
-   if (fmode == 0) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
- 
-@@ -2000,6 +2031,7 @@ MODRET xfer_pre_retr(cmd_rec *cmd) {
- 
-     /* Deliberately use EISDIR for anything non-file (e.g. directories). */
-     errno = EISDIR;
-+    cmd->error_code = EISDIR;
-     return PR_ERROR(cmd);
-   }
- 
-@@ -2014,12 +2046,14 @@ MODRET xfer_pre_retr(cmd_rec *cmd) {
-       cmd->arg);
-     session.restart_pos = 0L;
-     errno = EPERM;
-+    cmd->error_code = EPERM;
-     return PR_ERROR(cmd);
-   }
- 
-   /* Otherwise everthing is good */
-   if (pr_table_add(cmd->notes, "mod_xfer.retr-path",
-       pstrdup(cmd->pool, dir), 0) < 0) {
-+    cmd->error_code = errno;
-     if (errno != EEXIST) {
-       pr_log_pri(PR_LOG_NOTICE, "notice: error adding 'mod_xfer.retr-path': %s",
-         strerror(errno));
-@@ -2046,6 +2080,7 @@ MODRET xfer_retr(cmd_rec *cmd) {
-   retr_fh = pr_fsio_open(dir, O_RDONLY);
-   if (retr_fh == NULL) {
-     int xerrno = errno;
-+    cmd->error_code = errno;
- 
-     (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
-       "error opening '%s': %s", cmd->argv[0], session.user,
-@@ -2059,6 +2094,7 @@ MODRET xfer_retr(cmd_rec *cmd) {
-   if (pr_fsio_stat(dir, &st) < 0) {
-     /* Error stat'ing the file. */
-     int xerrno = errno;
-+    cmd->error_code = errno;
-     pr_fsio_close(retr_fh);
-     errno = xerrno;
- 
-@@ -2083,6 +2119,7 @@ MODRET xfer_retr(cmd_rec *cmd) {
-     if (pr_fsio_lseek(retr_fh, session.restart_pos,
-         SEEK_SET) == (off_t) -1) {
-       int xerrno = errno;
-+      cmd->error_code = errno;
-       pr_fsio_close(retr_fh);
-       errno = xerrno;
-       retr_fh = NULL;
-@@ -2143,6 +2180,7 @@ MODRET xfer_retr(cmd_rec *cmd) {
-     retr_abort();
- 
-     /* Set errno to EPERM ("Operation not permitted") */
-+    cmd->error_code = EPERM;
-     pr_data_abort(EPERM, FALSE);
-     return PR_ERROR(cmd);
-   }
-@@ -2174,6 +2212,7 @@ MODRET xfer_retr(cmd_rec *cmd) {
-        * is preserved; errno itself might be overwritten in retr_abort().
-        */
-       int xerrno = errno;
-+      cmd->error_code = errno;
- 
-       retr_abort();
-       pr_data_abort(xerrno, FALSE);
--- a/components/proftpd/patches/proftpd-manpage.patch	Tue Mar 10 11:12:15 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
---- a/src/proftpd.8.in
-+++ b/src/proftpd.8.in
-@@ -100,10 +100,12 @@ This is enabled by default, if the \fB--enable-ipv6\fP configure option is
- used.
- .SH FILES
- .PD 0
--.B @SBINDIR@/proftpd
-+.B /usr/lib/inet/proftpd
- .br
- .B @SYSCONFDIR@/proftpd.conf
- .br
-+.B @SYSCONFDIR@/ftpusers
-+.br
- .B @BINDIR@/ftpwho
- .br
- .B @BINDIR@/ftpcount
-
--- a/components/proftpd/patches/proftpd-pam.patch	Tue Mar 10 11:12:15 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-http://bugs.proftpd.org/show_bug.cgi?id=4070
-
---- proftpd-1.3.3e/include/auth.h	Tue Sep  8 22:34:03 2009
-+++ proftpd-1.3.3e-pam/include/auth.h	Tue May 24 10:37:40 2011
-@@ -59,6 +59,35 @@
- /* Account has been disabled */
- #define PR_AUTH_DISABLEDPWD		-5
- 
-+/* Insufficient credentials */
-+#define PR_AUTH_CRED_INSUFF		-6
-+
-+/* Unavailable user credentials */
-+#define PR_AUTH_CRED_UNAVAIL		-7
-+
-+/* Failure setting user credentials */
-+#define PR_AUTH_CRED_ERR		-8
-+
-+/* Unavailable authentication service */
-+#define PR_AUTH_UNAVAIL			-9
-+
-+/* Max retries reached */
-+#define PR_AUTH_MAXTRIES		-10
-+
-+/* Initialization of authentization failed */
-+#define PR_AUTH_INIT_FAIL		-11
-+
-+/* New auth token needed */
-+#define PR_AUTH_NEWTOK			-12
-+
-+#define PR_AUTH_OPEN_ERR		-15
-+#define PR_AUTH_SYMBOL_ERR		-16
-+#define PR_AUTH_SERVICE_ERR		-17
-+#define PR_AUTH_SYSTEM_ERR		-18
-+#define PR_AUTH_BUF_ERR			-19
-+#define PR_AUTH_CONV_ERR		-20
-+#define PR_AUTH_PERM_DENIED		-21
-+
- void pr_auth_setpwent(pool *);
- void pr_auth_endpwent(pool *);
- void pr_auth_setgrent(pool *);
---- proftpd-1.3.3e/modules/mod_auth.c	Mon Feb 21 03:36:38 2011
-+++ proftpd-1.3.3e-pam/modules/mod_auth.c	Tue May 24 11:32:55 2011
-@@ -898,6 +898,44 @@
-           user);
-         goto auth_failure;
- 
-+      case PR_AUTH_CRED_INSUFF:
-+        pr_log_auth(PR_LOG_NOTICE,
-+          "USER %s (Login failed): Insufficient credentials.", origuser);
-+        goto auth_failure;
-+
-+      case PR_AUTH_CRED_UNAVAIL:
-+        pr_log_auth(PR_LOG_NOTICE, 
-+          "USER %s (Login failed): Unavailable credentials.", origuser);
-+        goto auth_failure;
-+
-+
-+      case PR_AUTH_CRED_ERR:
-+        pr_log_auth(PR_LOG_NOTICE, 
-+          "USER %s (Login failed): Failure setting user credentials.",
-+          origuser);
-+        goto auth_failure;
-+
-+      case PR_AUTH_UNAVAIL:
-+        pr_log_auth(PR_LOG_NOTICE,
-+          "USER %s (Login failed): Unavailable authentication service.", user);
-+        goto auth_failure;
-+
-+      case PR_AUTH_MAXTRIES:
-+        pr_log_auth(PR_LOG_NOTICE,
-+          "USER %s (Login failed): Max retries reached.", user);
-+        goto auth_failure;
-+
-+      case PR_AUTH_INIT_FAIL:
-+        pr_log_auth(PR_LOG_NOTICE,
-+          "USER %s (Login failed): Authentization initialization failed.",
-+          origuser);
-+        goto auth_failure;
-+
-+      case PR_AUTH_NEWTOK:
-+        pr_log_auth(PR_LOG_NOTICE,
-+          "USER %s (Login failed): New authentication token needed.", user);
-+        goto auth_failure;
-+
-       default:
-         break;
-     };
---- proftpd-1.3.3e/modules/mod_auth_pam.c	Thu Mar  5 06:24:06 2009
-+++ proftpd-1.3.3e-pam/modules/mod_auth_pam.c	Tue May 24 10:28:58 2011
-@@ -349,6 +349,24 @@
- 
-   if (pam_error != PAM_SUCCESS) {
-     switch (pam_error) {
-+#ifdef PAM_CRED_INSUFFICIENT
-+      case PAM_CRED_INSUFFICIENT:
-+        retval = PR_AUTH_CRED_INSUFF;
-+        break;
-+#endif /* PAM_CRED_INSUFFICIENT */
-+
-+#ifdef PAM_AUTHINFO_UNAVAIL
-+      case PAM_AUTHINFO_UNAVAIL:
-+        retval = PR_AUTH_UNAVAIL;
-+        break;
-+#endif /* PAM_AUTHINFO_UNAVAIL */
-+
-+#ifdef PAM_MAXTRIES
-+      case PAM_MAXTRIES:
-+        retval = PR_AUTH_MAXTRIES;
-+        break;
-+#endif /* PAM_MAXTRIES */
-+
-       case PAM_USER_UNKNOWN:
-         retval = PR_AUTH_NOPWD;
-         break;
-@@ -373,6 +391,14 @@
- 
-   if (pam_error != PAM_SUCCESS) {
-     switch (pam_error) {
-+#ifdef PAM_NEW_AUTHTOK_REQD
-+      case PAM_NEW_AUTHTOK_REQD:
-+        pr_trace_msg(trace_channel, 8,
-+          "account mgmt error: PAM_NEW_AUTH_REQD");
-+        retval = PR_AUTH_NEWTOK;
-+        break;
-+#endif /* PAM_NEW_AUTHTOK_REQD */
-+
- #ifdef PAM_AUTHTOKEN_REQD
-       case PAM_AUTHTOKEN_REQD:
-         pr_trace_msg(trace_channel, 8,
-@@ -417,7 +443,7 @@
-     switch (pam_error) {
-       case PAM_SESSION_ERR:
-       default:
--        retval = PR_AUTH_DISABLEDPWD;
-+        retval = PR_AUTH_INIT_FAIL;
-         break;
-     }
- 
-@@ -435,6 +461,20 @@
- 
-   if (pam_error != PAM_SUCCESS) {
-     switch (pam_error) {
-+#ifdef PAM_CRED_UNAVAIL
-+      case PAM_CRED_UNAVAIL:
-+        pr_trace_msg(trace_channel, 8, "credentials error: PAM_CRED_UNAVAIL");
-+        retval = PR_AUTH_CRED_UNAVAIL;
-+        break;
-+#endif /* PAM_CRED_UNAVAIL */
-+
-+#ifdef PAM_CRED_ERR
-+      case PAM_CRED_ERR:
-+        pr_trace_msg(trace_channel, 8, "credentials error: PAM_CRED_ERR");
-+        retval = PR_AUTH_CRED_ERR;
-+        break;
-+#endif /* PAM_CRED_ERR */
-+
-       case PAM_CRED_EXPIRED:
-         pr_trace_msg(trace_channel, 8, "credentials error: PAM_CRED_EXPIRED");
-         retval = PR_AUTH_AGEPWD;
--- a/components/proftpd/patches/proftpd-retry.patch	Tue Mar 10 11:12:15 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
---- a/src/data.c
-+++ b/src/data.c
-@@ -337,6 +337,7 @@ static int data_pasv_open(char *reason, off_t size) {
- static int data_active_open(char *reason, off_t size) {
-   conn_t *c;
-   int bind_port, rev;
-+  int retries = 0;
-   pr_netaddr_t *bind_addr;
-   unsigned char *root_revoke = NULL;
- 
-@@ -368,7 +369,9 @@ static int data_active_open(char *reason, off_t size) {
-     bind_port = INPORT_ANY;
-   }
- 
--  session.d = pr_inet_create_conn(session.pool, -1, bind_addr, bind_port, TRUE);
-+  for (;;) {
-+    session.d = pr_inet_create_conn(session.pool, -1, bind_addr, bind_port,
-+      TRUE);
- 
-   /* Default remote address to which to connect for an active transfer,
-    * if the client has not specified a different address via PORT/EPRT,
-@@ -416,6 +419,15 @@ static int data_active_open(char *reason, off_t size) {
- 
-   if (pr_inet_connect(session.d->pool, session.d, &session.data_addr,
-       session.data_port) == -1) {
-+
-+    if (session.d->xerrno == EADDRINUSE && retries < 16) {
-+      destroy_pool(session.d->pool);
-+      pr_signals_handle();
-+      /* Wait up to MSL to avoid TIME_WAIT. */
-+      sleep(retries++);
-+      continue;
-+    }
-+
-     pr_log_debug(DEBUG6,
-       "Error connecting to %s#%u for active data transfer: %s",
-       pr_netaddr_get_ipstr(&session.data_addr), session.data_port,
-@@ -427,6 +439,9 @@ static int data_active_open(char *reason, off_t size) {
-     destroy_pool(session.d->pool);
-     session.d = NULL;
-     return -1;
-+  } else
-+    break;
-+
-   }
- 
-   c = pr_inet_openrw(session.pool, session.d, NULL, PR_NETIO_STRM_DATA,
-
--- a/components/proftpd/patches/proftpd-slow_ls.patch	Tue Mar 10 11:12:15 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-The fix was developed in-house and submitted upstream. The upstream bug
-is:
-
-http://bugs.proftpd.org/show_bug.cgi?id=4157
-
-diff --git a/modules/mod_core.c b/modules/mod_core.c
---- a/modules/mod_core.c
-+++ b/modules/mod_core.c
-@@ -40,6 +40,7 @@
- /* From src/main.c */
- extern unsigned long max_connects;
- extern unsigned int max_connect_interval;
-+extern unsigned char tracing_enabled;
- 
- /* From modules/mod_site.c */
- extern modret_t *site_dispatch(cmd_rec*);
-@@ -1381,6 +1382,8 @@
-   int per_session = FALSE;
-   unsigned int idx = 1;
- 
-+  tracing_enabled = TRUE;
-+
-   if (cmd->argc-1 < 1)
-     CONF_ERROR(cmd, "wrong number of parameters");
-   CHECK_CONF(cmd, CONF_ROOT);
-diff --git a/modules/mod_ls.c b/modules/mod_ls.c
---- a/modules/mod_ls.c
-+++ b/modules/mod_ls.c
-@@ -307,10 +307,12 @@
- 
- static int sendline(int flags, char *fmt, ...) {
-   va_list msg;
--  char buf[PR_TUNABLE_BUFFER_SIZE+1] = {'\0'};
-+  char buf[PR_TUNABLE_BUFFER_SIZE+1];
-   int res = 0;
-   size_t buflen, listbuflen;
- 
-+  (void) memset(buf, '\0', sizeof(buf));
-+
-   if (listbuf == NULL) {
-     listbufsz = pr_config_get_server_xfer_bufsz(PR_NETIO_IO_WR);
-     listbuf = listbuf_ptr = pcalloc(session.pool, listbufsz);
-diff --git a/src/fsio.c b/src/fsio.c
---- a/src/fsio.c
-+++ b/src/fsio.c
-@@ -556,10 +556,12 @@
- static int cache_stat(pr_fs_t *fs, const char *path, struct stat *sbuf,
-     unsigned int op) {
-   int res = -1;
--  char pathbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
-+  char pathbuf[PR_TUNABLE_PATH_MAX + 1];
-   int (*mystat)(pr_fs_t *, const char *, struct stat *) = NULL;
-   size_t pathlen;
- 
-+  (void) memset(pathbuf, '\0', sizeof(pathbuf));
-+
-   /* Sanity checks */
-   if (fs == NULL) {
-     errno = EINVAL;
-@@ -641,8 +643,8 @@
-  * during the hit.
-  */
- static pr_fs_t *lookup_dir_fs(const char *path, int op) {
--  char buf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
--  char tmp_path[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
-+  char buf[PR_TUNABLE_PATH_MAX + 1];
-+  char tmp_path[PR_TUNABLE_PATH_MAX + 1];
-   pr_fs_t *fs = NULL;
-   int exact = FALSE;
-   size_t tmp_pathlen = 0;
-@@ -651,6 +653,9 @@
-   pr_fs_match_t *fsm = NULL;
- #endif /* PR_FS_MATCH */
- 
-+  (void) memset(buf, '\0', sizeof(buf));
-+  (void) memset(tmp_path, '\0', sizeof(tmp_path));
-+
-   sstrncpy(buf, path, sizeof(buf));
- 
-   /* Check if the given path is an absolute path.  Since there may be
-diff --git a/src/trace.c b/src/trace.c
---- a/src/trace.c
-+++ b/src/trace.c
-@@ -32,6 +32,8 @@
- 
- #ifdef PR_USE_TRACE
- 
-+unsigned char tracing_enabled = FALSE;
-+
- static int trace_logfd = -1;
- static unsigned long trace_opts = PR_TRACE_OPT_DEFAULT;
- static pool *trace_pool = NULL;
-@@ -473,6 +475,10 @@
-   int res;
-   va_list msg;
- 
-+  /* Optimization: do not run tracing code unless explicitly enabled. */
-+  if (tracing_enabled == FALSE)
-+    return 0;
-+
-   va_start(msg, fmt);
-   res = pr_trace_vmsg(channel, level, fmt, msg);
-   va_end(msg);
--- a/components/proftpd/proftpd.p5m	Tue Mar 10 11:12:15 2015 -0700
+++ b/components/proftpd/proftpd.p5m	Wed Mar 11 03:50:20 2015 -0700
@@ -49,6 +49,7 @@
 file path=usr/bin/ftptop
 file path=usr/bin/ftpwho
 file path=usr/bin/prxs
+file dtrace/example.d path=usr/demo/dtrace/ftp.d
 file path=usr/include/proftpd/auth.h
 file path=usr/include/proftpd/bindings.h
 file path=usr/include/proftpd/buildstamp.h
@@ -106,6 +107,7 @@
 file path=usr/include/proftpd/var.h
 file path=usr/include/proftpd/version.h
 file path=usr/include/proftpd/xferlog.h
+file dtrace/ftp.d path=usr/lib/dtrace/ftp.d
 file ManageFTP.html path=usr/lib/help/auths/locale/C/ManageFTP.html
 file usr/sbin/proftpd path=usr/lib/inet/proftpd
 file path=usr/lib/pkgconfig/proftpd.pc