# HG changeset patch # User Tomas Klacko # Date 1432028075 25200 # Node ID 3a33895438c9fcbdd035ead56cb49ae5d23c5ec6 # Parent 427b52500a3aa4e06571a709dfb0ecc5e5db2494 20553228 add proftpd dtrace provider from AK to Userland diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/Makefile --- a/components/proftpd/Makefile Fri May 15 09:41:56 2015 -0700 +++ b/components/proftpd/Makefile Tue May 19 02:34:35 2015 -0700 @@ -20,7 +20,7 @@ # # -# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. # include ../../make-rules/shared-macros.mk @@ -56,7 +56,7 @@ # instead of 'e'. IPS_COMPONENT_VERSION= $(COMPONENT_VERSION) -CONFIGURE_OPTIONS += CFLAGS="$(CFLAGS) -I/usr/include/kerberosv5 -DHAVE_KRB5_H=1 -DKRB5_DLLIMP= -DHAVE__GETGRPSBYMEMBER" +CONFIGURE_OPTIONS += CFLAGS="$(CFLAGS) -I/usr/include/kerberosv5 -DHAVE_KRB5_H=1 -DKRB5_DLLIMP= -DHAVE__GETGRPSBYMEMBER -D_SOLARIS_DTRACE" # Force immediate binding because of chroot(). CONFIGURE_OPTIONS += LDFLAGS="-z guidance=nolazyload -z nolazyload -lbsm" CONFIGURE_OPTIONS += install_user=`id -nu` @@ -75,7 +75,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 ; \ @@ -88,6 +88,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 diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/dtrace/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/proftpd/dtrace/Makefile Tue May 19 02:34:35 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 diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/dtrace/example.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/proftpd/dtrace/example.d Tue May 19 02:34:35 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; +} diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/dtrace/ftp.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/proftpd/dtrace/ftp.d Tue May 19 02:34:35 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 { + ci_protocol = "tcp"; + ci_remote = copyinstr((uintptr_t) + *(uint32_t *)copyin((uintptr_t)&f->ftp_raddr, sizeof (uint32_t))); + ci_local = ""; +}; + +#pragma D binding "1.6.1" translator +translator ftpinfo_t { + 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)); +}; diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/dtrace/ftp_provider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/proftpd/dtrace/ftp_provider.d Tue May 19 02:34:35 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 { + dummy = 0; +}; + +translator ftpinfo_t { + 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 diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/dtrace/ftp_provider_impl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/proftpd/dtrace/ftp_provider_impl.c Tue May 19 02:34:35 2015 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + */ + +#include + +#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); +} diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/dtrace/ftp_provider_impl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/proftpd/dtrace/ftp_provider_impl.h Tue May 19 02:34:35 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 */ diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/000.proftpd-manpage.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/proftpd/patches/000.proftpd-manpage.patch Tue May 19 02:34:35 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 + diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/001.mod_auth_unix_getgrp.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/proftpd/patches/001.mod_auth_unix_getgrp.patch Tue May 19 02:34:35 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 */ + } + diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/002.proftpd-configuration-html.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/proftpd/patches/002.proftpd-configuration-html.patch Tue May 19 02:34:35 2015 -0700 @@ -0,0 +1,12 @@ +--- a/doc/Configuration.html ++++ b/doc/Configuration.html +@@ -10805,7 +10805,7 @@ CLASS="SYNOPSIS" + >

IdentLookups on

IdentLookups off

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,
+
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/005.proftpd-error_code.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/005.proftpd-error_code.patch	Tue May 19 02:34:35 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  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  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  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);
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/006.18845170.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/006.18845170.patch	Tue May 19 02:34:35 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'};
+
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/007.19575647.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/007.19575647.patch	Tue May 19 02:34:35 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;
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/008.19596338.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/008.19596338.patch	Tue May 19 02:34:35 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);
+       }
+     }
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/009.19668629.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/009.19668629.patch	Tue May 19 02:34:35 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));
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/010.20393612.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/010.20393612.patch	Tue May 19 02:34:35 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'};
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/011.19693019-proftpd-slow_ls.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/011.19693019-proftpd-slow_ls.patch	Tue May 19 02:34:35 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);
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/012.20553228-dtrace-provider.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/012.20553228-dtrace-provider.patch	Tue May 19 02:34:35 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 
+@@ -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;
+ 
+ 
+
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/18845170.patch
--- a/components/proftpd/patches/18845170.patch	Fri May 15 09:41:56 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'};
-
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/19575647.patch
--- a/components/proftpd/patches/19575647.patch	Fri May 15 09:41:56 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;
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/19596338.patch
--- a/components/proftpd/patches/19596338.patch	Fri May 15 09:41:56 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);
-       }
-     }
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/19668629.patch
--- a/components/proftpd/patches/19668629.patch	Fri May 15 09:41:56 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));
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/20393612.patch
--- a/components/proftpd/patches/20393612.patch	Fri May 15 09:41:56 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'};
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/mod_auth_unix_getgrp.patch
--- a/components/proftpd/patches/mod_auth_unix_getgrp.patch	Fri May 15 09:41:56 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 */
-   }
- 
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/proftpd-configuration-html.patch
--- a/components/proftpd/patches/proftpd-configuration-html.patch	Fri May 15 09:41:56 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"
- >

IdentLookups on

IdentLookups off

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  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  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  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);
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/proftpd-manpage.patch
--- a/components/proftpd/patches/proftpd-manpage.patch	Fri May 15 09:41:56 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
-
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/proftpd-pam.patch
--- a/components/proftpd/patches/proftpd-pam.patch	Fri May 15 09:41:56 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;
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/proftpd-retry.patch
--- a/components/proftpd/patches/proftpd-retry.patch	Fri May 15 09:41:56 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,
-
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/patches/proftpd-slow_ls.patch
--- a/components/proftpd/patches/proftpd-slow_ls.patch	Fri May 15 09:41:56 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);
diff -r 427b52500a3a -r 3a33895438c9 components/proftpd/proftpd.p5m
--- a/components/proftpd/proftpd.p5m	Fri May 15 09:41:56 2015 -0700
+++ b/components/proftpd/proftpd.p5m	Tue May 19 02:34:35 2015 -0700
@@ -18,82 +18,46 @@
 #
 # CDDL HEADER END
 #
-# Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
 #
 
 # PSARC/2011/088
  default mangler.man.stability uncommitted>
 set name=pkg.fmri \
     value=pkg:/service/network/ftp@$(IPS_COMPONENT_VERSION),$(BUILD_VERSION)
+set name=pkg.summary value="FTP Server and Utilities"
 set name=pkg.human-version value=$(HUMAN_VERSION)
-set name=pkg.summary value="FTP Server and Utilities"
-set name=info.classification value="org.opensolaris.category.2008:System/Core"
+set name=info.classification value=org.opensolaris.category.2008:System/Core
 set name=info.source-url value=$(COMPONENT_ARCHIVE_URL)
 set name=info.upstream-url value=$(COMPONENT_PROJECT_URL)
-set name=org.opensolaris.arc-caseid \
-    value=PSARC/2011/088
+set name=org.opensolaris.arc-caseid value=PSARC/2011/088
 set name=org.opensolaris.consolidation value=$(CONSOLIDATION)
-
-dir path=etc
-dir path=etc/security
-dir path=etc/security/auth_attr.d
-dir path=etc/security/prof_attr.d
-dir path=lib
-dir path=lib/svc
-dir path=lib/svc/manifest
-dir path=lib/svc/manifest/network
-dir path=lib/svc/method
-dir path=usr
-dir path=usr/bin
-dir path=usr/include
-dir path=usr/include/proftpd
-dir path=usr/lib
-dir path=usr/lib/help
-dir path=usr/lib/help/auths
-dir path=usr/lib/help/auths/locale
-dir path=usr/lib/help/auths/locale/C
-dir path=usr/lib/inet
-dir path=usr/lib/pkgconfig
-dir path=usr/lib/proftpd
-dir path=usr/sbin
-dir path=usr/share
-dir path=usr/share/doc
-dir path=usr/share/doc/proftpd
-dir path=usr/share/doc/proftpd/modules
-dir path=usr/share/locale
-dir path=usr/share/locale/bg_BG
-dir path=usr/share/locale/bg_BG/LC_MESSAGES
-dir path=usr/share/locale/en_US
-dir path=usr/share/locale/en_US/LC_MESSAGES
-dir path=usr/share/locale/fr
-dir path=usr/share/locale/fr/LC_MESSAGES
-dir path=usr/share/locale/it
-dir path=usr/share/locale/it/LC_MESSAGES
-dir path=usr/share/locale/ja
-dir path=usr/share/locale/ja/LC_MESSAGES
-dir path=usr/share/locale/ko
-dir path=usr/share/locale/ko/LC_MESSAGES
-dir path=usr/share/locale/ru
-dir path=usr/share/locale/ru/LC_MESSAGES
-dir path=usr/share/locale/zh_CN
-dir path=usr/share/locale/zh_CN/LC_MESSAGES
-dir path=usr/share/locale/zh_TW
-dir path=usr/share/locale/zh_TW/LC_MESSAGES
-dir path=usr/share/man
-dir path=usr/share/man/man1
-dir path=usr/share/man/man4
-dir path=usr/share/man/man5
-dir path=usr/share/man/man8
+dir  path=etc
 file proftpd.conf path=etc/proftpd.conf mode=0644 preserve=true
-file auth_service-network-ftpd path=etc/security/auth_attr.d/service-network-ftpd
-file prof_service-network-ftpd path=etc/security/prof_attr.d/service-network-ftpd
+dir  path=etc/security
+dir  path=etc/security/auth_attr.d
+file auth_service-network-ftpd \
+    path=etc/security/auth_attr.d/service-network-ftpd
+dir  path=etc/security/prof_attr.d
+file prof_service-network-ftpd \
+    path=etc/security/prof_attr.d/service-network-ftpd
+dir  path=lib
+dir  path=lib/svc
+dir  path=lib/svc/manifest
+dir  path=lib/svc/manifest/network
 file ftp.xml path=lib/svc/manifest/network/ftp.xml
+dir  path=lib/svc/method
 file svc-ftp path=lib/svc/method/svc-ftp
+dir  path=usr
+dir  path=usr/bin
 file path=usr/bin/ftpcount
 file path=usr/bin/ftpdctl
 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
+dir  path=usr/include
+dir  path=usr/include/proftpd
 file path=usr/include/proftpd/auth.h
 file path=usr/include/proftpd/bindings.h
 file path=usr/include/proftpd/buildstamp.h
@@ -151,65 +115,106 @@
 file path=usr/include/proftpd/var.h
 file path=usr/include/proftpd/version.h
 file path=usr/include/proftpd/xferlog.h
+dir  path=usr/lib
+file dtrace/ftp.d path=usr/lib/dtrace/ftp.d
+dir  path=usr/lib/help
+dir  path=usr/lib/help/auths
+dir  path=usr/lib/help/auths/locale
+dir  path=usr/lib/help/auths/locale/C
 file ManageFTP.html path=usr/lib/help/auths/locale/C/ManageFTP.html
+dir  path=usr/lib/inet
 file usr/sbin/proftpd path=usr/lib/inet/proftpd
+dir  path=usr/lib/pkgconfig
 file path=usr/lib/pkgconfig/proftpd.pc
+dir  path=usr/lib/proftpd
 file path=usr/lib/proftpd/mod_auth_gss.so
 file path=usr/lib/proftpd/mod_facl.so
 file path=usr/lib/proftpd/mod_gss.so
 file path=usr/lib/proftpd/mod_tls.so
 file path=usr/lib/proftpd/mod_wrap.so
+dir  path=usr/sbin
 file ftprestart.sh path=usr/sbin/ftprestart
 file path=usr/sbin/ftpscrub
 file path=usr/sbin/ftpshut
-file proftpd_migration.txt path=usr/share/doc/proftpd/proftpd_migration.txt
+dir  path=usr/share
+dir  path=usr/share/doc
+dir  path=usr/share/doc/proftpd
 file doc/Configuration.html path=usr/share/doc/proftpd/Configuration.html
-file doc/modules/mod_auth_file.html path=usr/share/doc/proftpd/modules/mod_auth_file.html
-file doc/modules/mod_auth_pam.html path=usr/share/doc/proftpd/modules/mod_auth_pam.html
-file doc/modules/mod_ctrls.html path=usr/share/doc/proftpd/modules/mod_ctrls.html
-file doc/modules/mod_delay.html path=usr/share/doc/proftpd/modules/mod_delay.html
+dir  path=usr/share/doc/proftpd/modules
+file doc/modules/mod_auth_file.html \
+    path=usr/share/doc/proftpd/modules/mod_auth_file.html
+file doc/modules/mod_auth_pam.html \
+    path=usr/share/doc/proftpd/modules/mod_auth_pam.html
+file doc/modules/mod_ctrls.html \
+    path=usr/share/doc/proftpd/modules/mod_ctrls.html
+file doc/modules/mod_delay.html \
+    path=usr/share/doc/proftpd/modules/mod_delay.html
 file doc/modules/mod_dso.html path=usr/share/doc/proftpd/modules/mod_dso.html
 file doc/modules/mod_facl.html path=usr/share/doc/proftpd/modules/mod_facl.html
-file doc/modules/mod_facts.html path=usr/share/doc/proftpd/modules/mod_facts.html
-file doc/modules/mod_ident.html path=usr/share/doc/proftpd/modules/mod_ident.html
+file doc/modules/mod_facts.html \
+    path=usr/share/doc/proftpd/modules/mod_facts.html
+file doc/contrib/mod_gss.html path=usr/share/doc/proftpd/modules/mod_gss.html
+file doc/modules/mod_ident.html \
+    path=usr/share/doc/proftpd/modules/mod_ident.html
 file doc/modules/mod_lang.html path=usr/share/doc/proftpd/modules/mod_lang.html
-file doc/contrib/mod_gss.html path=usr/share/doc/proftpd/modules/mod_gss.html
 file doc/contrib/mod_tls.html path=usr/share/doc/proftpd/modules/mod_tls.html
 file doc/contrib/mod_wrap.html path=usr/share/doc/proftpd/modules/mod_wrap.html
+file proftpd_migration.txt path=usr/share/doc/proftpd/proftpd_migration.txt
+dir  path=usr/share/locale
+dir  path=usr/share/locale/bg_BG
+dir  path=usr/share/locale/bg_BG/LC_MESSAGES
 file path=usr/share/locale/bg_BG/LC_MESSAGES/proftpd.mo
+dir  path=usr/share/locale/en_US
+dir  path=usr/share/locale/en_US/LC_MESSAGES
 file path=usr/share/locale/en_US/LC_MESSAGES/proftpd.mo
-file usr/share/locale/fr_FR/LC_MESSAGES/proftpd.mo path=usr/share/locale/fr/LC_MESSAGES/proftpd.mo
-file usr/share/locale/it_IT/LC_MESSAGES/proftpd.mo path=usr/share/locale/it/LC_MESSAGES/proftpd.mo
-file usr/share/locale/ja_JP/LC_MESSAGES/proftpd.mo path=usr/share/locale/ja/LC_MESSAGES/proftpd.mo
-file usr/share/locale/ko_KR/LC_MESSAGES/proftpd.mo path=usr/share/locale/ko/LC_MESSAGES/proftpd.mo
-file usr/share/locale/ru_RU/LC_MESSAGES/proftpd.mo path=usr/share/locale/ru/LC_MESSAGES/proftpd.mo
+dir  path=usr/share/locale/fr
+dir  path=usr/share/locale/fr/LC_MESSAGES
+file usr/share/locale/fr_FR/LC_MESSAGES/proftpd.mo \
+    path=usr/share/locale/fr/LC_MESSAGES/proftpd.mo
+dir  path=usr/share/locale/it
+dir  path=usr/share/locale/it/LC_MESSAGES
+file usr/share/locale/it_IT/LC_MESSAGES/proftpd.mo \
+    path=usr/share/locale/it/LC_MESSAGES/proftpd.mo
+dir  path=usr/share/locale/ja
+dir  path=usr/share/locale/ja/LC_MESSAGES
+file usr/share/locale/ja_JP/LC_MESSAGES/proftpd.mo \
+    path=usr/share/locale/ja/LC_MESSAGES/proftpd.mo
+dir  path=usr/share/locale/ko
+dir  path=usr/share/locale/ko/LC_MESSAGES
+file usr/share/locale/ko_KR/LC_MESSAGES/proftpd.mo \
+    path=usr/share/locale/ko/LC_MESSAGES/proftpd.mo
+dir  path=usr/share/locale/ru
+dir  path=usr/share/locale/ru/LC_MESSAGES
+file usr/share/locale/ru_RU/LC_MESSAGES/proftpd.mo \
+    path=usr/share/locale/ru/LC_MESSAGES/proftpd.mo
+dir  path=usr/share/locale/zh_CN
+dir  path=usr/share/locale/zh_CN/LC_MESSAGES
 file path=usr/share/locale/zh_CN/LC_MESSAGES/proftpd.mo
+dir  path=usr/share/locale/zh_TW
+dir  path=usr/share/locale/zh_TW/LC_MESSAGES
 file path=usr/share/locale/zh_TW/LC_MESSAGES/proftpd.mo
+dir  path=usr/share/man
+dir  path=usr/share/man/man1
 file path=usr/share/man/man1/ftpcount.1
 file path=usr/share/man/man1/ftptop.1
 file path=usr/share/man/man1/ftpwho.1
+dir  path=usr/share/man/man4
+dir  path=usr/share/man/man5
 file path=usr/share/man/man5/proftpd.conf.5
 file path=usr/share/man/man5/xferlog.5
+dir  path=usr/share/man/man8
 file path=usr/share/man/man8/ftpdctl.8
 file path=usr/share/man/man8/ftpscrub.8
 file path=usr/share/man/man8/ftpshut.8
 file path=usr/share/man/man8/proftpd.8
-
-group groupname="ftp" gid=21
-
-legacy	pkg=SUNWftpu \
-    name="FTP Server, (Usr)" \
-    desc="FTP Server and Utilities"
-
+group groupname=ftp gid=21
+user username=ftp ftpuser=false gcos-field="FTPD Reserved UID" group=ftp uid=21
+legacy pkg=SUNWftpu desc="FTP Server and Utilities" name="FTP Server, (Usr)"
 license proftpd.license license="GPLv2 (mod_gss)" \
     com.oracle.info.description="the ProFPTD GSS Engine" \
-    com.oracle.info.name=mod_gss \
-    com.oracle.info.tpno=13480 \
+    com.oracle.info.name=mod_gss com.oracle.info.tpno=13480 \
     com.oracle.info.version=1.3.6
 license proftpd.license license="GPLv2 (proftpd)" \
     com.oracle.info.description="the ProFTPD server and utilities" \
-    com.oracle.info.name=proftpd \
-    com.oracle.info.tpno=17783 \
+    com.oracle.info.name=proftpd com.oracle.info.tpno=17783 \
     com.oracle.info.version=1.3.5
-
-user ftpuser=false gcos-field="FTPD Reserved UID" username="ftp" uid=21 group="ftp"