selftest: run smbcacls test against a share with a DFS link
[samba.git] / selftest / target / Samba3.pm
index 447c1e8e3a720ed4af6091a44b8439bd67e578b2..2e2f74efb79764d787b5558b8ba88eee734b30a9 100755 (executable)
@@ -3,15 +3,27 @@
 # Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org>
 # Published under the GNU GPL, v3 or later.
 
+# NOTE: Refer to the README for more details about the various testenvs,
+# and tips about adding new testenvs.
+
 package Samba3;
 
 use strict;
+use warnings;
 use Cwd qw(abs_path);
 use FindBin qw($RealBin);
 use POSIX;
 use target::Samba;
 use File::Path 'remove_tree';
 
+sub return_alias_env
+{
+       my ($self, $path, $env) = @_;
+
+       # just an alias
+       return $env;
+}
+
 sub have_ads($) {
         my ($self) = @_;
        my $found_ads = 0;
@@ -46,12 +58,13 @@ sub get_fs_specific_conf($$)
                return "vfs objects = $mods";
        }
 
-       return undef;
+       return '';
 }
 
 sub new($$) {
-       my ($classname, $bindir, $srcdir, $server_maxtime) = @_;
+       my ($classname, $SambaCtx, $bindir, $srcdir, $server_maxtime) = @_;
        my $self = { vars => {},
+                    SambaCtx => $SambaCtx,
                     bindir => $bindir,
                     srcdir => $srcdir,
                     server_maxtime => $server_maxtime
@@ -61,6 +74,19 @@ sub new($$) {
 }
 
 sub teardown_env($$)
+{
+       my ($self, $envvars) = @_;
+
+       if (defined($envvars->{CTDB_PREFIX})) {
+               $self->teardown_env_ctdb($envvars);
+       } else {
+               $self->teardown_env_samba($envvars);
+       }
+
+       return;
+}
+
+sub teardown_env_samba($$)
 {
        my ($self, $envvars) = @_;
        my $count = 0;
@@ -120,6 +146,31 @@ sub teardown_env($$)
        return 0;
 }
 
+sub teardown_env_ctdb($$)
+{
+       my ($self, $data) = @_;
+
+       if (defined($data->{SAMBA_NODES})) {
+               my $num_nodes = $data->{NUM_NODES};
+               my $nodes = $data->{SAMBA_NODES};
+
+               for (my $i = 0; $i < $num_nodes; $i++) {
+                       if (defined($nodes->[$i])) {
+                               $self->teardown_env_samba($nodes->[$i]);
+                       }
+               }
+       }
+
+       close($data->{CTDB_STDIN_PIPE});
+
+       if (not defined($data->{SAMBA_NODES})) {
+               # Give waiting children time to exit
+               sleep(5);
+       }
+
+       return 0;
+}
+
 sub getlog_env_app($$$)
 {
        my ($self, $envvars, $name) = @_;
@@ -169,24 +220,33 @@ sub check_env($$)
 %Samba3::ENV_DEPS = (
        # name              => [dep_1, dep_2, ...],
        nt4_dc              => [],
+       nt4_dc_smb1         => [],
+       nt4_dc_smb1_done    => ["nt4_dc_smb1"],
        nt4_dc_schannel     => [],
 
        simpleserver        => [],
        fileserver          => [],
+       fileserver_smb1     => [],
+       fileserver_smb1_done => ["fileserver_smb1"],
        maptoguest          => [],
        ktest               => [],
 
        nt4_member          => ["nt4_dc"],
 
-       ad_member           => ["ad_dc"],
+       ad_member           => ["ad_dc", "fl2008r2dc", "fl2003dc"],
        ad_member_rfc2307   => ["ad_dc_ntvfs"],
        ad_member_idmap_rid => ["ad_dc"],
-       ad_member_idmap_ad  => ["ad_dc"],
+       ad_member_idmap_ad  => ["fl2008r2dc"],
+       ad_member_fips      => ["ad_dc_fips"],
+
+       clusteredmember_smb1 => ["nt4_dc"],
 );
 
+%Samba3::ENV_DEPS_POST = ();
+
 sub setup_nt4_dc
 {
-       my ($self, $path) = @_;
+       my ($self, $path, $more_conf, $server) = @_;
 
        print "PROVISIONING NT4 DC...";
 
@@ -214,14 +274,26 @@ sub setup_nt4_dc
        check parent directory delete on close = yes
 ";
 
-       my $vars = $self->provision($path, "SAMBA-TEST",
-                                   "LOCALNT4DC2",
-                                   "localntdc2pass",
-                                   $nt4_dc_options);
+       if (defined($more_conf)) {
+               $nt4_dc_options = $nt4_dc_options . $more_conf;
+       }
+       if (!defined($server)) {
+               $server = "LOCALNT4DC2";
+       }
+       my $vars = $self->provision(
+           prefix => $path,
+           domain => "SAMBA-TEST",
+           server => $server,
+           password => "localntdc2pass",
+           extra_options => $nt4_dc_options);
 
        $vars or return undef;
 
-       if (not $self->check_or_start($vars, "yes", "yes", "yes")) {
+       if (not $self->check_or_start(
+               env_vars => $vars,
+               nmbd => "yes",
+               winbindd => "yes",
+               smbd => "yes")) {
               return undef;
        }
 
@@ -236,6 +308,23 @@ sub setup_nt4_dc
        return $vars;
 }
 
+sub setup_nt4_dc_smb1
+{
+       my ($self, $path) = @_;
+       my $conf = "
+[global]
+       client min protocol = CORE
+       server min protocol = LANMAN1
+";
+       return $self->setup_nt4_dc($path, $conf, "LCLNT4DC2SMB1");
+}
+
+sub setup_nt4_dc_smb1_done
+{
+       my ($self, $path, $dep_env) = @_;
+       return $self->return_alias_env($path, $dep_env);
+}
+
 sub setup_nt4_dc_schannel
 {
        my ($self, $path) = @_;
@@ -263,14 +352,20 @@ sub setup_nt4_dc_schannel
        server max protocol = SMB2_02
 ";
 
-       my $vars = $self->provision($path, "NT4SCHANNEL",
-                                   "LOCALNT4DC9",
-                                   "localntdc9pass",
-                                   $pdc_options);
+       my $vars = $self->provision(
+           prefix => $path,
+           domain => "NT4SCHANNEL",
+           server => "LOCALNT4DC9",
+           password => "localntdc9pass",
+           extra_options => $pdc_options);
 
        $vars or return undef;
 
-       if (not $self->check_or_start($vars, "yes", "yes", "yes")) {
+       if (not $self->check_or_start(
+               env_vars => $vars,
+               nmbd => "yes",
+               winbindd => "yes",
+               smbd => "yes")) {
               return undef;
        }
 
@@ -294,17 +389,21 @@ sub setup_nt4_member
        print "PROVISIONING MEMBER...";
 
        my $require_mutexes = "dbwrap_tdb_require_mutexes:* = yes";
-       $require_mutexes = "" if ($ENV{SELFTEST_DONT_REQUIRE_TDB_MUTEX_SUPPORT} eq "1");
+       if ($ENV{SELFTEST_DONT_REQUIRE_TDB_MUTEX_SUPPORT} // '' eq "1") {
+               $require_mutexes = "";
+       }
 
        my $member_options = "
        security = domain
        dbwrap_tdb_mutexes:* = yes
        ${require_mutexes}
 ";
-       my $ret = $self->provision($prefix, $nt4_dc_vars->{DOMAIN},
-                                  "LOCALNT4MEMBER3",
-                                  "localnt4member3pass",
-                                  $member_options);
+       my $ret = $self->provision(
+           prefix => $prefix,
+           domain => $nt4_dc_vars->{DOMAIN},
+           server => "LOCALNT4MEMBER3",
+           password => "localnt4member3pass",
+           extra_options => $member_options);
 
        $ret or return undef;
 
@@ -324,7 +423,8 @@ sub setup_nt4_member
        }
 
        my $net = Samba::bindir_path($self, "net");
-       my $cmd = "";
+       # Add hosts file for name lookups
+       my $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' ";
        $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
        $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" ";
        $cmd .= "$net rpc join $ret->{CONFIGURATION} $nt4_dc_vars->{DOMAIN} member";
@@ -335,7 +435,8 @@ sub setup_nt4_member
            return undef;
        }
 
-       my $cmd = "";
+       # Add hosts file for name lookups
+       $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' ";
        $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
        $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" ";
        $cmd .= "$net $ret->{CONFIGURATION} primarytrust dumpinfo | grep -q 'REDACTED SECRET VALUES'";
@@ -345,7 +446,11 @@ sub setup_nt4_member
            return undef;
        }
 
-       if (not $self->check_or_start($ret, "yes", "yes", "yes")) {
+       if (not $self->check_or_start(
+               env_vars => $ret,
+               nmbd => "yes",
+               winbindd => "yes",
+               smbd => "yes")) {
               return undef;
        }
 
@@ -360,19 +465,196 @@ sub setup_nt4_member
        return $ret;
 }
 
-sub setup_ad_member
+sub setup_clusteredmember_smb1
 {
-       my ($self, $prefix, $dcvars) = @_;
+       my ($self, $prefix, $nt4_dc_vars) = @_;
+       my $count = 0;
+       my $rc;
+       my @retvals = ();
+       my $ret;
+
+       print "PROVISIONING CLUSTEREDMEMBER...\n";
 
        my $prefix_abs = abs_path($prefix);
-       my @dirs = ();
+       mkdir($prefix_abs, 0777);
 
-       # If we didn't build with ADS, pretend this env was never available
-       if (not $self->have_ads()) {
-               return "UNKNOWN";
+       my $server_name = "CLUSTEREDMEMBER";
+
+       my $ctdb_data = $self->setup_ctdb($prefix);
+
+       if (not $ctdb_data) {
+               print "No ctdb data\n";
+               return undef;
+       }
+
+       print "PROVISIONING CLUSTERED SAMBA...\n";
+
+       my $num_nodes = $ctdb_data->{NUM_NODES};
+       my $nodes = $ctdb_data->{CTDB_NODES};
+
+       # Enable cleanup of earlier nodes if a later node fails
+       $ctdb_data->{SAMBA_NODES} = \@retvals;
+
+       for (my $i = 0; $i < $num_nodes; $i++) {
+               my $node = $nodes->[$i];
+               my $socket = $node->{SOCKET_FILE};
+               my $server_name = $node->{SERVER_NAME};
+               my $pub_iface = $node->{SOCKET_WRAPPER_DEFAULT_IFACE};
+               my $node_prefix = $node->{NODE_PREFIX};
+
+               print "NODE_PREFIX=${node_prefix}\n";
+               print "SOCKET=${socket}\n";
+
+               my $require_mutexes = "dbwrap_tdb_require_mutexes:* = yes";
+               if ($ENV{SELFTEST_DONT_REQUIRE_TDB_MUTEX_SUPPORT} // '' eq "1") {
+                       $require_mutexes = "" ;
+               }
+
+               my $member_options = "
+       security = domain
+       server signing = on
+       clustering = yes
+       ctdbd socket = ${socket}
+       client min protocol = CORE
+       server min protocol = LANMAN1
+       dbwrap_tdb_mutexes:* = yes
+       ${require_mutexes}
+";
+
+               my $node_ret = $self->provision(
+                   prefix => "$node_prefix",
+                   domain => $nt4_dc_vars->{DOMAIN},
+                   server => "$server_name",
+                   password => "clustermember8pass",
+                   netbios_name => "CLUSTEREDMEMBER",
+                   share_dir => "${prefix_abs}/shared",
+                   extra_options => $member_options,
+                   no_delete_prefix => 1);
+               if (not $node_ret) {
+                       print "Provision node $i failed\n";
+                       teardown_env($self, $ctdb_data);
+                       return undef;
+               }
+
+               my $nmblookup = Samba::bindir_path($self, "nmblookup");
+               do {
+                       print "Waiting for the LOGON SERVER registration ...\n";
+                       $rc = system("$nmblookup $node_ret->{CONFIGURATION} " .
+                                    "$node_ret->{DOMAIN}\#1c");
+                       if ($rc != 0) {
+                               sleep(1);
+                       }
+                       $count++;
+               } while ($rc != 0 && $count < 10);
+
+               if ($count == 10) {
+                       print "NMBD not reachable after 10 retries\n";
+                       teardown_env($self, $node_ret);
+                       teardown_env($self, $ctdb_data);
+                       return undef;
+               }
+
+               push(@retvals, $node_ret);
+       }
+
+       $ret = {%$ctdb_data, %{$retvals[0]}};
+
+       my $net = Samba::bindir_path($self, "net");
+       my $cmd = "";
+       $cmd .= "UID_WRAPPER_ROOT=1 ";
+       $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
+       $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" ";
+       $cmd .= "$net join $ret->{CONFIGURATION} $nt4_dc_vars->{DOMAIN} member";
+       $cmd .= " -U$nt4_dc_vars->{USERNAME}\%$nt4_dc_vars->{PASSWORD}";
+
+       if (system($cmd) != 0) {
+               warn("Join failed\n$cmd");
+               teardown_env($self, $ret);
+               return undef;
+       }
+
+       for (my $i=0; $i<@retvals; $i++) {
+               my $node_provision = $retvals[$i];
+               my $ok;
+               $ok = $self->check_or_start(
+                   env_vars => $node_provision,
+                   winbindd => "yes",
+                   smbd => "yes",
+                   child_cleanup => sub {
+                       map {
+                           my $fh = $_->{STDIN_PIPE};
+                           close($fh) if defined($fh);
+                       } @retvals });
+               if (not $ok) {
+                       teardown_env($self, $ret);
+                       return undef;
+               }
+       }
+
+       #
+       # Build a unclist for every share
+       #
+       unless (open(NODES, "<$ret->{CTDB_NODES_FILE}")) {
+               warn("Unable to open CTDB nodes file");
+               teardown_env($self, $ret);
+               return undef;
+       }
+       my @nodes = <NODES>;
+       close(NODES);
+       chomp @nodes;
+
+       my $conffile = $ret->{SERVERCONFFILE};
+       $cmd = "";
+       $cmd .= 'sed -n -e \'s|^\[\(.*\)\]$|\1|p\'';
+       $cmd .= " \"$conffile\"";
+       $cmd .= " | grep -vx 'global'";
+
+       my @shares = `$cmd`;
+       $rc = $?;
+       if ($rc != 0) {
+               warn("Listing shares failed\n$cmd");
+               teardown_env($self, $ret);
+               return undef;
+       }
+       chomp @shares;
+
+       my $unclistdir = "${prefix_abs}/unclists";
+       mkdir($unclistdir, 0777);
+       foreach my $share (@shares) {
+               my $l = "${unclistdir}/${share}.txt";
+               unless (open(UNCLIST, ">${l}")) {
+                       warn("Unable to open UNC list ${l}");
+                       teardown_env($self, $ret);
+                       return undef;
+               }
+               foreach my $node (@nodes) {
+                       print UNCLIST "//${node}/${share}\n";
+               }
+               close(UNCLIST);
        }
 
-       print "PROVISIONING S3 AD MEMBER...";
+       $ret->{DOMSID} = $nt4_dc_vars->{DOMSID};
+       $ret->{DC_SERVER} = $nt4_dc_vars->{SERVER};
+       $ret->{DC_SERVER_IP} = $nt4_dc_vars->{SERVER_IP};
+       $ret->{DC_SERVER_IPV6} = $nt4_dc_vars->{SERVER_IPV6};
+       $ret->{DC_NETBIOSNAME} = $nt4_dc_vars->{NETBIOSNAME};
+       $ret->{DC_USERNAME} = $nt4_dc_vars->{USERNAME};
+       $ret->{DC_PASSWORD} = $nt4_dc_vars->{PASSWORD};
+
+       return $ret;
+}
+
+sub provision_ad_member
+{
+       my ($self,
+           $prefix,
+           $dcvars,
+           $trustvars_f,
+           $trustvars_e,
+           $force_fips_mode) = @_;
+
+       my $prefix_abs = abs_path($prefix);
+       my @dirs = ();
 
        mkdir($prefix_abs, 0777);
 
@@ -407,6 +689,22 @@ sub setup_ad_member
         realm = $dcvars->{REALM}
         netbios aliases = foo bar
        template homedir = /home/%D/%G/%U
+       auth event notification = true
+       password server = $dcvars->{SERVER}
+       winbind scan trusted domains = no
+       winbind use krb5 enterprise principals = yes
+
+       allow dcerpc auth level connect:lsarpc = yes
+       dcesrv:max auth states = 8
+
+       rpc_server:epmapper = external
+       rpc_server:lsarpc = external
+       rpc_server:samr = external
+       rpc_server:netlogon = disabled
+       rpc_server:register_embedded_np = yes
+
+       rpc_daemon:epmd = fork
+       rpc_daemon:lsasd = fork
 
 [sub_dug]
        path = $share_dir/D_%D/U_%U/G_%G
@@ -418,12 +716,13 @@ sub setup_ad_member
 
 ";
 
-       my $ret = $self->provision($prefix, $dcvars->{DOMAIN},
-                                  "LOCALADMEMBER",
-                                  "loCalMemberPass",
-                                  $member_options,
-                                  $dcvars->{SERVER_IP},
-                                  $dcvars->{SERVER_IPV6});
+       my $ret = $self->provision(
+           prefix => $prefix,
+           domain => $dcvars->{DOMAIN},
+           server => "LOCALADMEMBER",
+           password => "loCalMemberPass",
+           extra_options => $member_options,
+           resolv_conf => $dcvars->{RESOLV_CONF});
 
        $ret or return undef;
 
@@ -447,18 +746,29 @@ sub setup_ad_member
 
        $ret->{KRB5_CONFIG} = $ctx->{krb5_conf};
 
+       if (defined($force_fips_mode)) {
+               $ret->{GNUTLS_FORCE_FIPS_MODE} = "1";
+               $ret->{OPENSSL_FORCE_FIPS_MODE} = "1";
+       }
+
        my $net = Samba::bindir_path($self, "net");
-       my $cmd = "";
+       # Add hosts file for name lookups
+       my $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' ";
        $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
        if (defined($ret->{RESOLV_WRAPPER_CONF})) {
                $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" ";
        } else {
                $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" ";
        }
+       if (defined($force_fips_mode)) {
+               $cmd .= "GNUTLS_FORCE_FIPS_MODE=1 ";
+               $cmd .= "OPENSSL_FORCE_FIPS_MODE=1 ";
+       }
+       $cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
        $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
        $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" ";
        $cmd .= "$net join $ret->{CONFIGURATION}";
-       $cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}";
+       $cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD} -k";
 
        if (system($cmd) != 0) {
            warn("Join failed\n$cmd");
@@ -470,20 +780,63 @@ sub setup_ad_member
        # access the share for tests.
        chmod 0777, "$prefix/share";
 
-       if (not $self->check_or_start($ret, "yes", "yes", "yes")) {
+       if (not $self->check_or_start(
+               env_vars => $ret,
+               nmbd => "yes",
+               winbindd => "yes",
+               smbd => "yes")) {
                return undef;
        }
 
        $ret->{DC_SERVER} = $dcvars->{SERVER};
        $ret->{DC_SERVER_IP} = $dcvars->{SERVER_IP};
        $ret->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6};
+       $ret->{DC_SERVERCONFFILE} = $dcvars->{SERVERCONFFILE};
        $ret->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME};
        $ret->{DC_USERNAME} = $dcvars->{USERNAME};
        $ret->{DC_PASSWORD} = $dcvars->{PASSWORD};
 
+       # forest trust
+       $ret->{TRUST_F_BOTH_SERVER} = $trustvars_f->{SERVER};
+       $ret->{TRUST_F_BOTH_SERVER_IP} = $trustvars_f->{SERVER_IP};
+       $ret->{TRUST_F_BOTH_SERVER_IPV6} = $trustvars_f->{SERVER_IPV6};
+       $ret->{TRUST_F_BOTH_NETBIOSNAME} = $trustvars_f->{NETBIOSNAME};
+       $ret->{TRUST_F_BOTH_USERNAME} = $trustvars_f->{USERNAME};
+       $ret->{TRUST_F_BOTH_PASSWORD} = $trustvars_f->{PASSWORD};
+       $ret->{TRUST_F_BOTH_DOMAIN} = $trustvars_f->{DOMAIN};
+       $ret->{TRUST_F_BOTH_REALM} = $trustvars_f->{REALM};
+
+       # external trust
+       $ret->{TRUST_E_BOTH_SERVER} = $trustvars_e->{SERVER};
+       $ret->{TRUST_E_BOTH_SERVER_IP} = $trustvars_e->{SERVER_IP};
+       $ret->{TRUST_E_BOTH_SERVER_IPV6} = $trustvars_e->{SERVER_IPV6};
+       $ret->{TRUST_E_BOTH_NETBIOSNAME} = $trustvars_e->{NETBIOSNAME};
+       $ret->{TRUST_E_BOTH_USERNAME} = $trustvars_e->{USERNAME};
+       $ret->{TRUST_E_BOTH_PASSWORD} = $trustvars_e->{PASSWORD};
+       $ret->{TRUST_E_BOTH_DOMAIN} = $trustvars_e->{DOMAIN};
+       $ret->{TRUST_E_BOTH_REALM} = $trustvars_e->{REALM};
+
        return $ret;
 }
 
+sub setup_ad_member
+{
+       my ($self,
+           $prefix,
+           $dcvars,
+           $trustvars_f,
+           $trustvars_e) = @_;
+
+       # If we didn't build with ADS, pretend this env was never available
+       if (not $self->have_ads()) {
+               return "UNKNOWN";
+       }
+
+       print "PROVISIONING AD MEMBER...";
+
+       return $self->provision_ad_member($prefix, $dcvars, $trustvars_f, $trustvars_e);
+}
+
 sub setup_ad_member_rfc2307
 {
        my ($self, $prefix, $dcvars) = @_;
@@ -513,12 +866,13 @@ sub setup_ad_member_rfc2307
         password server = $dcvars->{SERVER}
 ";
 
-       my $ret = $self->provision($prefix, $dcvars->{DOMAIN},
-                                  "RFC2307MEMBER",
-                                  "loCalMemberPass",
-                                  $member_options,
-                                  $dcvars->{SERVER_IP},
-                                  $dcvars->{SERVER_IPV6});
+       my $ret = $self->provision(
+           prefix => $prefix,
+           domain => $dcvars->{DOMAIN},
+           server => "RFC2307MEMBER",
+           password => "loCalMemberPass",
+           extra_options => $member_options,
+           resolv_conf => $dcvars->{RESOLV_CONF});
 
        $ret or return undef;
 
@@ -542,13 +896,15 @@ sub setup_ad_member_rfc2307
        $ret->{KRB5_CONFIG} = $ctx->{krb5_conf};
 
        my $net = Samba::bindir_path($self, "net");
-       my $cmd = "";
+       # Add hosts file for name lookups
+       my $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' ";
        $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
        if (defined($ret->{RESOLV_WRAPPER_CONF})) {
                $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" ";
        } else {
                $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" ";
        }
+       $cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
        $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
        $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" ";
        $cmd .= "$net join $ret->{CONFIGURATION}";
@@ -564,7 +920,11 @@ sub setup_ad_member_rfc2307
        # access the share for tests.
        chmod 0777, "$prefix/share";
 
-       if (not $self->check_or_start($ret, "yes", "yes", "yes")) {
+       if (not $self->check_or_start(
+               env_vars => $ret,
+               nmbd => "yes",
+               winbindd => "yes",
+               smbd => "yes")) {
                return undef;
        }
 
@@ -597,14 +957,19 @@ sub setup_ad_member_idmap_rid
        idmap config * : range = 1000000-1999999
        idmap config $dcvars->{DOMAIN} : backend = rid
        idmap config $dcvars->{DOMAIN} : range = 2000000-2999999
+       # Prevent overridding the provisioned lib/krb5.conf which sets certain
+       # values required for tests to succeed
+       create krb5 conf = no
+        map to guest = bad user
 ";
 
-       my $ret = $self->provision($prefix, $dcvars->{DOMAIN},
-                                  "IDMAPRIDMEMBER",
-                                  "loCalMemberPass",
-                                  $member_options,
-                                  $dcvars->{SERVER_IP},
-                                  $dcvars->{SERVER_IPV6});
+       my $ret = $self->provision(
+           prefix => $prefix,
+           domain => $dcvars->{DOMAIN},
+           server => "IDMAPRIDMEMBER",
+           password => "loCalMemberPass",
+           extra_options => $member_options,
+           resolv_conf => $dcvars->{RESOLV_CONF});
 
        $ret or return undef;
 
@@ -628,13 +993,15 @@ sub setup_ad_member_idmap_rid
        $ret->{KRB5_CONFIG} = $ctx->{krb5_conf};
 
        my $net = Samba::bindir_path($self, "net");
-       my $cmd = "";
+       # Add hosts file for name lookups
+       my $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' ";
        $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
        if (defined($ret->{RESOLV_WRAPPER_CONF})) {
                $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" ";
        } else {
                $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" ";
        }
+       $cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
        $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
        $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" ";
        $cmd .= "$net join $ret->{CONFIGURATION}";
@@ -650,7 +1017,11 @@ sub setup_ad_member_idmap_rid
        # access the share for tests.
        chmod 0777, "$prefix/share";
 
-       if (not $self->check_or_start($ret, "yes", "yes", "yes")) {
+       if (not $self->check_or_start(
+               env_vars => $ret,
+               nmbd => "yes",
+               winbindd => "yes",
+               smbd => "yes")) {
                return undef;
        }
 
@@ -684,14 +1055,17 @@ sub setup_ad_member_idmap_ad
        idmap config * : range = 1000000-1999999
        idmap config $dcvars->{DOMAIN} : backend = ad
        idmap config $dcvars->{DOMAIN} : range = 2000000-2999999
+       idmap config $dcvars->{TRUST_DOMAIN} : backend = ad
+       idmap config $dcvars->{TRUST_DOMAIN} : range = 2000000-2999999
 ";
 
-       my $ret = $self->provision($prefix, $dcvars->{DOMAIN},
-                                  "IDMAPADMEMBER",
-                                  "loCalMemberPass",
-                                  $member_options,
-                                  $dcvars->{SERVER_IP},
-                                  $dcvars->{SERVER_IPV6});
+       my $ret = $self->provision(
+           prefix => $prefix,
+           domain => $dcvars->{DOMAIN},
+           server => "IDMAPADMEMBER",
+           password => "loCalMemberPass",
+           extra_options => $member_options,
+           resolv_conf => $dcvars->{RESOLV_CONF});
 
        $ret or return undef;
 
@@ -715,13 +1089,15 @@ sub setup_ad_member_idmap_ad
        $ret->{KRB5_CONFIG} = $ctx->{krb5_conf};
 
        my $net = Samba::bindir_path($self, "net");
-       my $cmd = "";
+       # Add hosts file for name lookups
+       my $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' ";
        $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
        if (defined($ret->{RESOLV_WRAPPER_CONF})) {
                $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" ";
        } else {
                $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" ";
        }
+       $cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
        $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
        $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" ";
        $cmd .= "$net join $ret->{CONFIGURATION}";
@@ -737,7 +1113,11 @@ sub setup_ad_member_idmap_ad
        # access the share for tests.
        chmod 0777, "$prefix/share";
 
-       if (not $self->check_or_start($ret, "yes", "yes", "yes")) {
+       if (not $self->check_or_start(
+               env_vars => $ret,
+               nmbd => "yes",
+               winbindd => "yes",
+               smbd => "yes")) {
                return undef;
        }
 
@@ -748,9 +1128,38 @@ sub setup_ad_member_idmap_ad
        $ret->{DC_USERNAME} = $dcvars->{USERNAME};
        $ret->{DC_PASSWORD} = $dcvars->{PASSWORD};
 
+       $ret->{TRUST_SERVER} = $dcvars->{TRUST_SERVER};
+       $ret->{TRUST_USERNAME} = $dcvars->{TRUST_USERNAME};
+       $ret->{TRUST_PASSWORD} = $dcvars->{TRUST_PASSWORD};
+       $ret->{TRUST_DOMAIN} = $dcvars->{TRUST_DOMAIN};
+       $ret->{TRUST_REALM} = $dcvars->{TRUST_REALM};
+       $ret->{TRUST_DOMSID} = $dcvars->{TRUST_DOMSID};
+
        return $ret;
 }
 
+sub setup_ad_member_fips
+{
+       my ($self,
+           $prefix,
+           $dcvars,
+           $trustvars_f,
+           $trustvars_e) = @_;
+
+       # If we didn't build with ADS, pretend this env was never available
+       if (not $self->have_ads()) {
+               return "UNKNOWN";
+       }
+
+       print "PROVISIONING AD FIPS MEMBER...";
+
+       return $self->provision_ad_member($prefix,
+                                         $dcvars,
+                                         $trustvars_f,
+                                         $trustvars_e,
+                                         1);
+}
+
 sub setup_simpleserver
 {
        my ($self, $path) = @_;
@@ -762,14 +1171,10 @@ sub setup_simpleserver
        my $simpleserver_options = "
        lanman auth = yes
        ntlm auth = yes
-       vfs objects = xattr_tdb streams_depot time_audit full_audit
+       vfs objects = xattr_tdb streams_depot
        change notify = no
        smb encrypt = off
 
-       full_audit:syslog = no
-       full_audit:success = none
-       full_audit:failure = none
-
 [vfs_aio_pthread]
        path = $prefix_abs/share
        read only = no
@@ -836,25 +1241,46 @@ sub setup_simpleserver
        path = $prefix_abs/share
        vfs objects =
        smb encrypt = desired
+
+[hidenewfiles]
+       path = $prefix_abs/share
+       hide new files timeout = 5
 ";
 
-       my $vars = $self->provision($path, "WORKGROUP",
-                                   "LOCALSHARE4",
-                                   "local4pass",
-                                   $simpleserver_options);
+       my $vars = $self->provision(
+           prefix => $path,
+           domain => "WORKGROUP",
+           server => "LOCALSHARE4",
+           password => "local4pass",
+           extra_options => $simpleserver_options);
 
        $vars or return undef;
 
-       if (not $self->check_or_start($vars, "yes", "no", "yes")) {
+       if (not $self->check_or_start(
+               env_vars => $vars,
+               nmbd => "yes",
+               smbd => "yes")) {
               return undef;
        }
 
        return $vars;
 }
 
+sub create_file_chmod($$)
+{
+    my ($name, $mode) = @_;
+    my $fh;
+
+    unless (open($fh, '>', $name)) {
+       warn("Unable to open $name");
+       return undef;
+    }
+    chmod($mode, $fh);
+}
+
 sub setup_fileserver
 {
-       my ($self, $path) = @_;
+       my ($self, $path, $more_conf, $server) = @_;
        my $prefix_abs = abs_path($path);
        my $srcdir_abs = abs_path($self->{srcdir});
 
@@ -903,11 +1329,26 @@ sub setup_fileserver
        my $tarmode_sharedir="$share_dir/tarmode";
        push(@dirs,$tarmode_sharedir);
 
+       my $smbcacls_sharedir="$share_dir/smbcacls";
+       push(@dirs,$smbcacls_sharedir);
+
        my $usershare_sharedir="$share_dir/usershares";
        push(@dirs,$usershare_sharedir);
 
+       my $dropbox_sharedir="$share_dir/dropbox";
+       push(@dirs,$dropbox_sharedir);
+
+       my $bad_iconv_sharedir="$share_dir/bad_iconv";
+       push(@dirs, $bad_iconv_sharedir);
+
+       my $ip4 = Samba::get_ipv4_addr("FILESERVER");
        my $fileserver_options = "
        kernel change notify = yes
+       rpc_server:mdssvc = embedded
+       spotlight backend = elasticsearch
+       elasticsearch:address = $ip4
+       elasticsearch:port = 8080
+       elasticsearch:mappings = $srcdir_abs/source3/rpc_server/mdssvc/elasticsearch_mappings.json
 
        usershare path = $usershare_dir
        usershare max shares = 10
@@ -916,6 +1357,14 @@ sub setup_fileserver
 
        get quota command = $prefix_abs/getset_quota.py
        set quota command = $prefix_abs/getset_quota.py
+[spotlight]
+       path = $share_dir
+       spotlight = yes
+       read only = no
+[no_spotlight]
+       path = $share_dir
+       spotlight = no
+       read only = no
 [lowercase]
        path = $lower_case_share_dir
        comment = smb username is [%U]
@@ -968,19 +1417,57 @@ sub setup_fileserver
        comment = inherit only unix owner
        inherit owner = unix only
        acl_xattr:ignore system acls = yes
+# BUG: https://bugzilla.samba.org/show_bug.cgi?id=13690
+[force_group_test]
+       path = $share_dir
+       comment = force group test
+#      force group = everyone
+
+[create_mode_664]
+       path = $share_dir
+       comment = smb username is [%U]
+       create mask = 0644
+       force create mode = 0664
+       vfs objects = dirsort
+
+[dropbox]
+       path = $dropbox_sharedir
+       comment = smb username is [%U]
+       writeable = yes
+       vfs objects =
+
+[bad_iconv]
+       path = $bad_iconv_sharedir
+       comment = smb username is [%U]
+       vfs objects =
+
+[homes]
+       comment = Home directories
+       browseable = No
+       read only = No
 ";
 
-       my $vars = $self->provision($path, "WORKGROUP",
-                                   "FILESERVER",
-                                   "fileserver",
-                                   $fileserver_options,
-                                   undef,
-                                   undef,
-                                   1);
+       if (defined($more_conf)) {
+               $fileserver_options = $fileserver_options . $more_conf;
+       }
+       if (!defined($server)) {
+               $server = "FILESERVER";
+       }
+
+       my $vars = $self->provision(
+           prefix => $path,
+           domain => "WORKGROUP",
+           server => $server,
+           password => "fileserver",
+           extra_options => $fileserver_options,
+           no_delete_prefix => 1);
 
        $vars or return undef;
 
-       if (not $self->check_or_start($vars, "yes", "no", "yes")) {
+       if (not $self->check_or_start(
+               env_vars => $vars,
+               nmbd => "yes",
+               smbd => "yes")) {
               return undef;
        }
 
@@ -1024,17 +1511,92 @@ sub setup_fileserver
        ##
        ## create a listable file in valid_users_share
        ##
-        my $valid_users_target = "$valid_users_sharedir/foo";
-        unless (open(VALID_USERS_TARGET, ">$valid_users_target")) {
-                warn("Unable to open $valid_users_target");
-                return undef;
-        }
-        close(VALID_USERS_TARGET);
-        chmod 0644, $valid_users_target;
+       create_file_chmod("$valid_users_sharedir/foo", 0644) or return undef;
+
+       ##
+       ## create a valid utf8 filename which is invalid as a CP850 conversion
+       ##
+       create_file_chmod("$bad_iconv_sharedir/\xED\x9F\xBF", 0644) or return undef;
 
        return $vars;
 }
 
+sub setup_fileserver_smb1
+{
+       my ($self, $path) = @_;
+       my $prefix_abs = abs_path($path);
+       my $conf = "
+[global]
+       client min protocol = CORE
+       server min protocol = LANMAN1
+
+[hidenewfiles]
+       path = $prefix_abs/share
+       hide new files timeout = 5
+[vfs_aio_pthread]
+       path = $prefix_abs/share
+       read only = no
+       vfs objects = aio_pthread
+       aio_pthread:aio open = yes
+       smbd:async dosmode = no
+
+[vfs_aio_pthread_async_dosmode_default1]
+       path = $prefix_abs/share
+       read only = no
+       vfs objects = aio_pthread
+       store dos attributes = yes
+       aio_pthread:aio open = yes
+       smbd:async dosmode = yes
+
+[vfs_aio_pthread_async_dosmode_default2]
+       path = $prefix_abs/share
+       read only = no
+       vfs objects = aio_pthread xattr_tdb
+       store dos attributes = yes
+       aio_pthread:aio open = yes
+       smbd:async dosmode = yes
+
+[vfs_aio_pthread_async_dosmode_force_sync1]
+       path = $prefix_abs/share
+       read only = no
+       vfs objects = aio_pthread
+       store dos attributes = yes
+       aio_pthread:aio open = yes
+       smbd:async dosmode = yes
+       # This simulates non linux systems
+       smbd:force sync user path safe threadpool = yes
+       smbd:force sync user chdir safe threadpool = yes
+       smbd:force sync root path safe threadpool = yes
+       smbd:force sync root chdir safe threadpool = yes
+
+[vfs_aio_pthread_async_dosmode_force_sync2]
+       path = $prefix_abs/share
+       read only = no
+       vfs objects = aio_pthread xattr_tdb
+       store dos attributes = yes
+       aio_pthread:aio open = yes
+       smbd:async dosmode = yes
+       # This simulates non linux systems
+       smbd:force sync user path safe threadpool = yes
+       smbd:force sync user chdir safe threadpool = yes
+       smbd:force sync root path safe threadpool = yes
+       smbd:force sync root chdir safe threadpool = yes
+
+[vfs_aio_fork]
+       path = $prefix_abs/share
+        vfs objects = aio_fork
+        read only = no
+        vfs_aio_fork:erratic_testing_mode=yes
+";
+       return $self->setup_fileserver($path, $conf, "FILESERVERSMB1");
+}
+
+sub setup_fileserver_smb1_done
+{
+       my ($self, $path, $dep_env) = @_;
+       return $self->return_alias_env($path, $dep_env);
+}
+
 sub setup_ktest
 {
        my ($self, $prefix) = @_;
@@ -1060,10 +1622,12 @@ sub setup_ktest
         ntlm auth = disabled
 ";
 
-       my $ret = $self->provision($prefix, "KTEST",
-                                  "LOCALKTEST6",
-                                  "localktest6pass",
-                                  $ktest_options);
+       my $ret = $self->provision(
+           prefix => $prefix,
+           domain => "KTEST",
+           server => "LOCALKTEST6",
+           password => "localktest6pass",
+           extra_options => $ktest_options);
 
        $ret or return undef;
 
@@ -1135,7 +1699,10 @@ $ret->{USERNAME} = KTEST\\Administrator
        # access the share for tests.
        chmod 0777, "$prefix/share";
 
-       if (not $self->check_or_start($ret, "yes", "no", "yes")) {
+       if (not $self->check_or_start(
+               env_vars => $ret,
+               nmbd => "yes",
+               smbd => "yes")) {
               return undef;
        }
        return $ret;
@@ -1152,14 +1719,19 @@ map to guest = bad user
 ntlm auth = yes
 ";
 
-       my $vars = $self->provision($path, "WORKGROUP",
-                                   "maptoguest",
-                                   "maptoguestpass",
-                                   $options);
+       my $vars = $self->provision(
+           prefix => $path,
+           domain => "WORKGROUP",
+           server => "maptoguest",
+           password => "maptoguestpass",
+           extra_options => $options);
 
        $vars or return undef;
 
-       if (not $self->check_or_start($vars, "yes", "no", "yes")) {
+       if (not $self->check_or_start(
+               env_vars => $vars,
+               nmbd => "yes",
+               smbd => "yes")) {
               return undef;
        }
 
@@ -1195,205 +1767,122 @@ sub read_pid($$)
        return $pid;
 }
 
-sub check_or_start($$$$$) {
-       my ($self, $env_vars, $nmbd, $winbindd, $smbd) = @_;
+# builds up the cmd args to run an s3 binary (i.e. smbd, nmbd, etc)
+sub make_bin_cmd
+{
+       my ($self, $binary, $env_vars, $options, $valgrind, $dont_log_stdout) = @_;
 
-       # use a pipe for stdin in the child processes. This allows
-       # those processes to monitor the pipe for EOF to ensure they
-       # exit when the test script exits
-       pipe(STDIN_READER, $env_vars->{STDIN_PIPE});
-
-       unlink($env_vars->{NMBD_TEST_LOG});
-       print "STARTING NMBD...";
-       my $pid = fork();
-       if ($pid == 0) {
-               open STDOUT, ">$env_vars->{NMBD_TEST_LOG}";
-               open STDERR, '>&STDOUT';
-
-               SocketWrapper::set_default_iface($env_vars->{SOCKET_WRAPPER_DEFAULT_IFACE});
-
-               $ENV{KRB5_CONFIG} = $env_vars->{KRB5_CONFIG};
-               $ENV{KRB5CCNAME} = "$env_vars->{KRB5_CCACHE}.nmbd";
-               $ENV{SELFTEST_WINBINDD_SOCKET_DIR} = $env_vars->{SELFTEST_WINBINDD_SOCKET_DIR};
-               $ENV{NMBD_SOCKET_DIR} = $env_vars->{NMBD_SOCKET_DIR};
-
-               $ENV{NSS_WRAPPER_PASSWD} = $env_vars->{NSS_WRAPPER_PASSWD};
-               $ENV{NSS_WRAPPER_GROUP} = $env_vars->{NSS_WRAPPER_GROUP};
-               $ENV{NSS_WRAPPER_HOSTS} = $env_vars->{NSS_WRAPPER_HOSTS};
-               $ENV{NSS_WRAPPER_HOSTNAME} = $env_vars->{NSS_WRAPPER_HOSTNAME};
-               $ENV{NSS_WRAPPER_MODULE_SO_PATH} = $env_vars->{NSS_WRAPPER_MODULE_SO_PATH};
-               $ENV{NSS_WRAPPER_MODULE_FN_PREFIX} = $env_vars->{NSS_WRAPPER_MODULE_FN_PREFIX};
-               $ENV{UID_WRAPPER_ROOT} = "1";
-
-               $ENV{ENVNAME} = "$ENV{ENVNAME}.nmbd";
-
-               if ($nmbd ne "yes") {
-                       $SIG{USR1} = $SIG{ALRM} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = sub {
-                               my $signame = shift;
-                               print("Skip nmbd received signal $signame");
-                               exit 0;
-                       };
-                       sleep($self->{server_maxtime});
-                       exit 0;
-               }
+       my @optargs = ("-d0");
+       if (defined($options)) {
+               @optargs = split(/ /, $options);
+       }
+       my @preargs = (Samba::bindir_path($self, "timelimit"), $self->{server_maxtime});
 
-               $ENV{MAKE_TEST_BINARY} = Samba::bindir_path($self, "nmbd");
-               my @optargs = ("-d0");
-               if (defined($ENV{NMBD_OPTIONS})) {
-                       @optargs = split(/ /, $ENV{NMBD_OPTIONS});
-               }
-               my @preargs = (Samba::bindir_path($self, "timelimit"), $self->{server_maxtime});
-               if(defined($ENV{NMBD_VALGRIND})) { 
-                       @preargs = split(/ /, $ENV{NMBD_VALGRIND});
-               }
-               my @args = ("-F", "--no-process-group",
-                           "-s", $env_vars->{SERVERCONFFILE},
-                           "-l", $env_vars->{LOGDIR});
-               if (not defined($ENV{NMBD_DONT_LOG_STDOUT})) {
-                       push(@args, "--log-stdout");
-               }
+       if (defined($valgrind)) {
+               @preargs = split(/ /, $valgrind);
+       }
+       my @args = ("-F", "--no-process-group",
+                   "-s", $env_vars->{SERVERCONFFILE},
+                   "-l", $env_vars->{LOGDIR});
+
+       if (not defined($dont_log_stdout)) {
+               push(@args, "--log-stdout");
+       }
+       return (@preargs, $binary, @args, @optargs);
+}
+
+sub check_or_start($$) {
+       my ($self, %args) = @_;
+       my $env_vars = $args{env_vars};
+       my $nmbd = $args{nmbd} // "no";
+       my $winbindd = $args{winbindd} // "no";
+       my $smbd = $args{smbd} // "no";
+       my $child_cleanup = $args{child_cleanup};
 
-               close($env_vars->{STDIN_PIPE});
-               open STDIN, ">&", \*STDIN_READER or die "can't dup STDIN_READER to STDIN: $!";
+       my $STDIN_READER;
 
-               exec(@preargs, $ENV{MAKE_TEST_BINARY}, @args, @optargs)
-                       or die("Unable to start $ENV{MAKE_TEST_BINARY}: $!");
+       # use a pipe for stdin in the child processes. This allows
+       # those processes to monitor the pipe for EOF to ensure they
+       # exit when the test script exits
+       pipe($STDIN_READER, $env_vars->{STDIN_PIPE});
+
+       my $binary = Samba::bindir_path($self, "nmbd");
+       my @full_cmd = $self->make_bin_cmd($binary, $env_vars,
+                                          $ENV{NMBD_OPTIONS}, $ENV{NMBD_VALGRIND},
+                                          $ENV{NMBD_DONT_LOG_STDOUT});
+       my $nmbd_envs = Samba::get_env_for_process("nmbd", $env_vars);
+       delete $nmbd_envs->{RESOLV_WRAPPER_CONF};
+       delete $nmbd_envs->{RESOLV_WRAPPER_HOSTS};
+
+       # fork and exec() nmbd in the child process
+       my $daemon_ctx = {
+               NAME => "nmbd",
+               BINARY_PATH => $binary,
+               FULL_CMD => [ @full_cmd ],
+               LOG_FILE => $env_vars->{NMBD_TEST_LOG},
+               PCAP_FILE => "env-$ENV{ENVNAME}-nmbd",
+               ENV_VARS => $nmbd_envs,
+       };
+       if ($nmbd ne "yes") {
+               $daemon_ctx->{SKIP_DAEMON} = 1;
        }
+       my $pid = Samba::fork_and_exec(
+           $self, $env_vars, $daemon_ctx, $STDIN_READER, $child_cleanup);
+
        $env_vars->{NMBD_TL_PID} = $pid;
        write_pid($env_vars, "nmbd", $pid);
-       print "DONE\n";
-
-       unlink($env_vars->{WINBINDD_TEST_LOG});
-       print "STARTING WINBINDD...";
-       $pid = fork();
-       if ($pid == 0) {
-               open STDOUT, ">$env_vars->{WINBINDD_TEST_LOG}";
-               open STDERR, '>&STDOUT';
-
-               SocketWrapper::set_default_iface($env_vars->{SOCKET_WRAPPER_DEFAULT_IFACE});
-
-               $ENV{KRB5_CONFIG} = $env_vars->{KRB5_CONFIG};
-               $ENV{KRB5CCNAME} = "$env_vars->{KRB5_CCACHE}.winbindd";
-               $ENV{SELFTEST_WINBINDD_SOCKET_DIR} = $env_vars->{SELFTEST_WINBINDD_SOCKET_DIR};
-               $ENV{NMBD_SOCKET_DIR} = $env_vars->{NMBD_SOCKET_DIR};
-
-               $ENV{NSS_WRAPPER_PASSWD} = $env_vars->{NSS_WRAPPER_PASSWD};
-               $ENV{NSS_WRAPPER_GROUP} = $env_vars->{NSS_WRAPPER_GROUP};
-               $ENV{NSS_WRAPPER_HOSTS} = $env_vars->{NSS_WRAPPER_HOSTS};
-               $ENV{NSS_WRAPPER_HOSTNAME} = $env_vars->{NSS_WRAPPER_HOSTNAME};
-               $ENV{NSS_WRAPPER_MODULE_SO_PATH} = $env_vars->{NSS_WRAPPER_MODULE_SO_PATH};
-               $ENV{NSS_WRAPPER_MODULE_FN_PREFIX} = $env_vars->{NSS_WRAPPER_MODULE_FN_PREFIX};
-               if (defined($env_vars->{RESOLV_WRAPPER_CONF})) {
-                       $ENV{RESOLV_WRAPPER_CONF} = $env_vars->{RESOLV_WRAPPER_CONF};
-               } else {
-                       $ENV{RESOLV_WRAPPER_HOSTS} = $env_vars->{RESOLV_WRAPPER_HOSTS};
-               }
-               $ENV{UID_WRAPPER_ROOT} = "1";
-
-               $ENV{ENVNAME} = "$ENV{ENVNAME}.winbindd";
-
-               if ($winbindd ne "yes") {
-                       $SIG{USR1} = $SIG{ALRM} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = sub {
-                               my $signame = shift;
-                               print("Skip winbindd received signal $signame");
-                               exit 0;
-                       };
-                       sleep($self->{server_maxtime});
-                       exit 0;
-               }
 
-               $ENV{MAKE_TEST_BINARY} = Samba::bindir_path($self, "winbindd");
-               my @optargs = ("-d0");
-               if (defined($ENV{WINBINDD_OPTIONS})) {
-                       @optargs = split(/ /, $ENV{WINBINDD_OPTIONS});
-               }
-               my @preargs = (Samba::bindir_path($self, "timelimit"), $self->{server_maxtime});
-               if(defined($ENV{WINBINDD_VALGRIND})) {
-                       @preargs = split(/ /, $ENV{WINBINDD_VALGRIND});
-               }
-               my @args = ("-F", "--no-process-group",
-                           "-s", $env_vars->{SERVERCONFFILE},
-                           "-l", $env_vars->{LOGDIR});
-               if (not defined($ENV{WINBINDD_DONT_LOG_STDOUT})) {
-                       push(@args, "--stdout");
-               }
+       $binary = Samba::bindir_path($self, "winbindd");
+       @full_cmd = $self->make_bin_cmd($binary, $env_vars,
+                                        $ENV{WINBINDD_OPTIONS}, $ENV{WINBINDD_VALGRIND}, "N/A");
 
-               close($env_vars->{STDIN_PIPE});
-               open STDIN, ">&", \*STDIN_READER or die "can't dup STDIN_READER to STDIN: $!";
+       if (not defined($ENV{WINBINDD_DONT_LOG_STDOUT})) {
+               push(@full_cmd, "--stdout");
+       }
 
-               exec(@preargs, $ENV{MAKE_TEST_BINARY}, @args, @optargs)
-                       or die("Unable to start $ENV{MAKE_TEST_BINARY}: $!");
+       # fork and exec() winbindd in the child process
+       $daemon_ctx = {
+               NAME => "winbindd",
+               BINARY_PATH => $binary,
+               FULL_CMD => [ @full_cmd ],
+               LOG_FILE => $env_vars->{WINBINDD_TEST_LOG},
+               PCAP_FILE => "env-$ENV{ENVNAME}-winbindd",
+       };
+       if ($winbindd ne "yes") {
+               $daemon_ctx->{SKIP_DAEMON} = 1;
        }
+
+       $pid = Samba::fork_and_exec(
+           $self, $env_vars, $daemon_ctx, $STDIN_READER, $child_cleanup);
+
        $env_vars->{WINBINDD_TL_PID} = $pid;
        write_pid($env_vars, "winbindd", $pid);
-       print "DONE\n";
-
-       unlink($env_vars->{SMBD_TEST_LOG});
-       print "STARTING SMBD...";
-       $pid = fork();
-       if ($pid == 0) {
-               open STDOUT, ">$env_vars->{SMBD_TEST_LOG}";
-               open STDERR, '>&STDOUT';
-
-               SocketWrapper::set_default_iface($env_vars->{SOCKET_WRAPPER_DEFAULT_IFACE});
-
-               $ENV{KRB5_CONFIG} = $env_vars->{KRB5_CONFIG};
-               $ENV{KRB5CCNAME} = "$env_vars->{KRB5_CCACHE}.smbd";
-               $ENV{SELFTEST_WINBINDD_SOCKET_DIR} = $env_vars->{SELFTEST_WINBINDD_SOCKET_DIR};
-               $ENV{NMBD_SOCKET_DIR} = $env_vars->{NMBD_SOCKET_DIR};
-
-               $ENV{NSS_WRAPPER_PASSWD} = $env_vars->{NSS_WRAPPER_PASSWD};
-               $ENV{NSS_WRAPPER_GROUP} = $env_vars->{NSS_WRAPPER_GROUP};
-               $ENV{NSS_WRAPPER_HOSTS} = $env_vars->{NSS_WRAPPER_HOSTS};
-               $ENV{NSS_WRAPPER_HOSTNAME} = $env_vars->{NSS_WRAPPER_HOSTNAME};
-               $ENV{NSS_WRAPPER_MODULE_SO_PATH} = $env_vars->{NSS_WRAPPER_MODULE_SO_PATH};
-               $ENV{NSS_WRAPPER_MODULE_FN_PREFIX} = $env_vars->{NSS_WRAPPER_MODULE_FN_PREFIX};
-               if (defined($env_vars->{RESOLV_WRAPPER_CONF})) {
-                       $ENV{RESOLV_WRAPPER_CONF} = $env_vars->{RESOLV_WRAPPER_CONF};
-               } else {
-                       $ENV{RESOLV_WRAPPER_HOSTS} = $env_vars->{RESOLV_WRAPPER_HOSTS};
-               }
-               $ENV{UID_WRAPPER_ROOT} = "1";
-
-               $ENV{ENVNAME} = "$ENV{ENVNAME}.smbd";
-
-               if ($smbd ne "yes") {
-                       $SIG{USR1} = $SIG{ALRM} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = sub {
-                               my $signame = shift;
-                               print("Skip smbd received signal $signame");
-                               exit 0;
-                       };
-                       sleep($self->{server_maxtime});
-                       exit 0;
-               }
 
-               $ENV{MAKE_TEST_BINARY} = Samba::bindir_path($self, "smbd");
-               my @optargs = ("-d0");
-               if (defined($ENV{SMBD_OPTIONS})) {
-                       @optargs = split(/ /, $ENV{SMBD_OPTIONS});
-               }
-               my @preargs = (Samba::bindir_path($self, "timelimit"), $self->{server_maxtime});
-               if(defined($ENV{SMBD_VALGRIND})) {
-                       @preargs = split(/ /,$ENV{SMBD_VALGRIND});
-               }
-               my @args = ("-F", "--no-process-group",
-                           "-s", $env_vars->{SERVERCONFFILE},
-                           "-l", $env_vars->{LOGDIR});
-               if (not defined($ENV{SMBD_DONT_LOG_STDOUT})) {
-                       push(@args, "--log-stdout");
-               }
+       $binary = Samba::bindir_path($self, "smbd");
+       @full_cmd = $self->make_bin_cmd($binary, $env_vars,
+                                        $ENV{SMBD_OPTIONS}, $ENV{SMBD_VALGRIND},
+                                        $ENV{SMBD_DONT_LOG_STDOUT});
+
+       # fork and exec() smbd in the child process
+       $daemon_ctx = {
+               NAME => "smbd",
+               BINARY_PATH => $binary,
+               FULL_CMD => [ @full_cmd ],
+               LOG_FILE => $env_vars->{SMBD_TEST_LOG},
+               PCAP_FILE => "env-$ENV{ENVNAME}-smbd",
+       };
+       if ($smbd ne "yes") {
+               $daemon_ctx->{SKIP_DAEMON} = 1;
+       }
 
-               close($env_vars->{STDIN_PIPE});
-               open STDIN, ">&", \*STDIN_READER or die "can't dup STDIN_READER to STDIN: $!";
+       $pid = Samba::fork_and_exec(
+           $self, $env_vars, $daemon_ctx, $STDIN_READER, $child_cleanup);
 
-               exec(@preargs, $ENV{MAKE_TEST_BINARY}, @args, @optargs)
-                       or die("Unable to start $ENV{MAKE_TEST_BINARY}: $!");
-       }
        $env_vars->{SMBD_TL_PID} = $pid;
        write_pid($env_vars, "smbd", $pid);
-       print "DONE\n";
 
-       close(STDIN_READER);
+       # close the parent's read-end of the pipe
+       close($STDIN_READER);
 
        return $self->wait_for_start($env_vars, $nmbd, $winbindd, $smbd);
 }
@@ -1419,9 +1908,18 @@ sub createuser($$$$$)
        }
 }
 
-sub provision($$$$$$$$$)
+sub provision($$)
 {
-       my ($self, $prefix, $domain, $server, $password, $extra_options, $dc_server_ip, $dc_server_ipv6, $no_delete_prefix) = @_;
+        my ($self, %args) = @_;
+
+       my $prefix = $args{prefix};
+       my $domain = $args{domain};
+       my $server = $args{server};
+       my $password = $args{password};
+       my $extra_options = $args{extra_options};
+       my $resolv_conf = $args{resolv_conf};
+       my $no_delete_prefix= $args{no_delete_prefix};
+       my $netbios_name = $args{netbios_name} // $server;
 
        ##
        ## setup the various environment variables we need
@@ -1431,8 +1929,8 @@ sub provision($$$$$$$$$)
        my $swiface = Samba::get_interface($server);
        my %ret = ();
        my %createuser_env = ();
-       my $server_ip = "127.0.0.$swiface";
-       my $server_ipv6 = sprintf("fd00:0000:0000:0000:0000:0000:5357:5f%02x", $swiface);
+       my $server_ip = Samba::get_ipv4_addr($server);
+       my $server_ipv6 = Samba::get_ipv6_addr($server);
 
        my $unix_name = ($ENV{USER} or $ENV{LOGNAME} or `PATH=/usr/ucb:$ENV{PATH} whoami`);
        chomp $unix_name;
@@ -1445,7 +1943,7 @@ sub provision($$$$$$$$$)
 
        my @dirs = ();
 
-       my $shrdir="$prefix_abs/share";
+       my $shrdir=$args{share_dir} // "$prefix_abs/share";
        push(@dirs,$shrdir);
 
        my $libdir="$prefix_abs/lib";
@@ -1457,6 +1955,9 @@ sub provision($$$$$$$$$)
        my $privatedir="$prefix_abs/private";
        push(@dirs,$privatedir);
 
+       my $cachedir = "$prefix_abs/cachedir";
+       push(@dirs, $cachedir);
+
        my $binddnsdir = "$prefix_abs/bind-dns";
        push(@dirs, $binddnsdir);
 
@@ -1481,12 +1982,18 @@ sub provision($$$$$$$$$)
        my $ro_shrdir="$shrdir/root-tmp";
        push(@dirs,$ro_shrdir);
 
+       my $noperm_shrdir="$shrdir/noperm-tmp";
+       push(@dirs,$noperm_shrdir);
+
        my $msdfs_shrdir="$shrdir/msdfsshare";
        push(@dirs,$msdfs_shrdir);
 
        my $msdfs_deeppath="$msdfs_shrdir/deeppath";
        push(@dirs,$msdfs_deeppath);
 
+       my $smbcacls_sharedir_dfs="$shrdir/smbcacls_sharedir_dfs";
+       push(@dirs,$smbcacls_sharedir_dfs);
+
        my $badnames_shrdir="$shrdir/badnames";
        push(@dirs,$badnames_shrdir);
 
@@ -1505,6 +2012,9 @@ sub provision($$$$$$$$$)
        my $widelinks_linkdir="$shrdir/widelinks_foo";
        push(@dirs,$widelinks_linkdir);
 
+       my $fsrvp_shrdir="$shrdir/fsrvp";
+       push(@dirs,$fsrvp_shrdir);
+
        my $shadow_tstdir="$shrdir/shadow";
        push(@dirs,$shadow_tstdir);
        my $shadow_mntdir="$shadow_tstdir/mount";
@@ -1548,59 +2058,40 @@ sub provision($$$$$$$$$)
        chmod 0755, $piddir;
 
 
+       ##
+       ## Create a directory without permissions to enter
+       ##
+       chmod 0000, $noperm_shrdir;
+
        ##
        ## create ro and msdfs share layout
        ##
 
        chmod 0755, $ro_shrdir;
-       my $unreadable_file = "$ro_shrdir/unreadable_file";
-       unless (open(UNREADABLE_FILE, ">$unreadable_file")) {
-               warn("Unable to open $unreadable_file");
-               return undef;
-       }
-       close(UNREADABLE_FILE);
-       chmod 0600, $unreadable_file;
 
-       my $msdfs_target = "$ro_shrdir/msdfs-target";
-       unless (open(MSDFS_TARGET, ">$msdfs_target")) {
-               warn("Unable to open $msdfs_target");
-               return undef;
-       }
-       close(MSDFS_TARGET);
-       chmod 0666, $msdfs_target;
+       create_file_chmod("$ro_shrdir/unreadable_file", 0600) or return undef;
+
+       create_file_chmod("$ro_shrdir/msdfs-target", 0600) or return undef;
        symlink "msdfs:$server_ip\\ro-tmp,$server_ipv6\\ro-tmp",
                "$msdfs_shrdir/msdfs-src1";
        symlink "msdfs:$server_ipv6\\ro-tmp", "$msdfs_shrdir/deeppath/msdfs-src2";
+       symlink "msdfs:$server_ip\\smbcacls_sharedir_dfs,$server_ipv6\\smbcacls_sharedir_dfs",
+               "$msdfs_shrdir/smbcacls_sharedir_dfs";
 
        ##
        ## create bad names in $badnames_shrdir
        ##
        ## (An invalid name, would be mangled to 8.3).
-        my $badname_target = "$badnames_shrdir/\340|\231\216\377\177";
-        unless (open(BADNAME_TARGET, ">$badname_target")) {
-                warn("Unable to open $badname_target");
-                return undef;
-        }
-        close(BADNAME_TARGET);
-        chmod 0666, $badname_target;
+       create_file_chmod("$badnames_shrdir/\340|\231\216\377\177",
+                         0600) or return undef;
 
        ## (A bad name, would not be mangled to 8.3).
-        my $badname_target = "$badnames_shrdir/\240\276\346\327\377\177";
-        unless (open(BADNAME_TARGET, ">$badname_target")) {
-                warn("Unable to open $badname_target");
-                return undef;
-        }
-        close(BADNAME_TARGET);
-        chmod 0666, $badname_target;
+       create_file_chmod("$badnames_shrdir/\240\276\346\327\377\177",
+                         0666) or return undef;
 
        ## (A bad good name).
-        my $badname_target = "$badnames_shrdir/blank.txt";
-        unless (open(BADNAME_TARGET, ">$badname_target")) {
-                warn("Unable to open $badname_target");
-                return undef;
-        }
-        close(BADNAME_TARGET);
-        chmod 0666, $badname_target;
+       create_file_chmod("$badnames_shrdir/blank.txt",
+                         0666) or return undef;
 
        ##
        ## create mangleable directory names in $manglenames_shrdir
@@ -1612,12 +2103,8 @@ sub provision($$$$$$$$$)
        ## create symlinks for widelinks tests.
        ##
        my $widelinks_target = "$widelinks_linkdir/target";
-       unless (open(WIDELINKS_TARGET, ">$widelinks_target")) {
-               warn("Unable to open $widelinks_target");
-               return undef;
-       }
-       close(WIDELINKS_TARGET);
-       chmod 0666, $widelinks_target;
+       create_file_chmod("$widelinks_target", 0666) or return undef;
+
        ##
        ## This link should get ACCESS_DENIED
        ##
@@ -1629,12 +2116,14 @@ sub provision($$$$$$$$$)
 
        my $conffile="$libdir/server.conf";
        my $dfqconffile="$libdir/dfq.conf";
+       my $errorinjectconf="$libdir/error_inject.conf";
+       my $delayinjectconf="$libdir/delay_inject.conf";
+       my $globalinjectconf="$libdir/global_inject.conf";
 
        my $nss_wrapper_pl = "$ENV{PERL} $self->{srcdir}/third_party/nss_wrapper/nss_wrapper.pl";
        my $nss_wrapper_passwd = "$privatedir/passwd";
        my $nss_wrapper_group = "$privatedir/group";
        my $nss_wrapper_hosts = "$ENV{SELFTEST_PREFIX}/hosts";
-       my $resolv_conf = "$privatedir/resolv.conf";
        my $dns_host_file = "$ENV{SELFTEST_PREFIX}/dns_host_file";
 
        my $mod_printer_pl = "$ENV{PERL} $self->{srcdir}/source3/script/tests/printing/modprinter.pl";
@@ -1657,8 +2146,11 @@ sub provision($$$$$$$$$)
        my ($gid_force_user);
        my ($uid_user1);
        my ($uid_user2);
+       my ($uid_gooduser);
+       my ($uid_eviluser);
+       my ($uid_slashuser);
 
-       if ($unix_uid < 0xffff - 10) {
+       if ($unix_uid < 0xffff - 13) {
                $max_uid = 0xffff;
        } else {
                $max_uid = $unix_uid;
@@ -1674,6 +2166,9 @@ sub provision($$$$$$$$$)
        $uid_smbget = $max_uid - 8;
        $uid_user1 = $max_uid - 9;
        $uid_user2 = $max_uid - 10;
+       $uid_gooduser = $max_uid - 11;
+       $uid_eviluser = $max_uid - 12;
+       $uid_slashuser = $max_uid - 13;
 
        if ($unix_gids[0] < 0xffff - 8) {
                $max_gid = 0xffff;
@@ -1698,14 +2193,21 @@ sub provision($$$$$$$$$)
                warn("Unable to open $conffile");
                return undef;
        }
+
+       my $interfaces = Samba::get_interfaces_config($server);
+
        print CONF "
 [global]
-       netbios name = $server
-       interfaces = $server_ip/8 $server_ipv6/64
+        dcesrv:fuzz directory = $cachedir/fuzz
+       netbios name = $netbios_name
+       interfaces = $interfaces
        bind interfaces only = yes
        panic action = cd $self->{srcdir} && $self->{srcdir}/selftest/gdb_backtrace %d %\$(MAKE_TEST_BINARY)
        smbd:suicide mode = yes
 
+       client min protocol = SMB2_02
+       server min protocol = SMB2_02
+
        workgroup = $domain
 
        private dir = $privatedir
@@ -1766,7 +2268,12 @@ sub provision($$$$$$$$$)
        dos filemode = yes
        strict rename = yes
        strict sync = yes
-       vfs objects = acl_xattr fake_acls xattr_tdb streams_depot
+       mangled names = yes
+       vfs objects = acl_xattr fake_acls xattr_tdb streams_depot time_audit full_audit
+
+       full_audit:syslog = no
+       full_audit:success = none
+       full_audit:failure = none
 
        printing = vlp
        print command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb print %p %s
@@ -1800,6 +2307,8 @@ sub provision($$$$$$$$$)
        #it just means we ALLOW one to be configured.
        allow insecure wide links = yes
 
+       include = $globalinjectconf
+
        # Begin extra options
        $extra_options
        # End extra options
@@ -1812,6 +2321,9 @@ sub provision($$$$$$$$$)
        }
 
        print CONF "
+[smbcacls_sharedir_dfs]
+       path = $smbcacls_sharedir_dfs
+        comment = smb username is [%U]
 [tmp]
        path = $shrdir
         comment = smb username is [%U]
@@ -1852,6 +2364,10 @@ sub provision($$$$$$$$$)
 [ro-tmp]
        path = $ro_shrdir
        guest ok = yes
+[noperm]
+       path = $noperm_shrdir
+       wide links = yes
+       guest ok = yes
 [write-list-tmp]
        path = $shrdir
         read only = yes
@@ -1930,6 +2446,24 @@ sub provision($$$$$$$$$)
        nfs4acl_xattr:encoding = xdr
        nfs4acl_xattr:version = 41
 
+[nfs4acl_nfs_40]
+       path = $shrdir
+       comment = smb username is [%U]
+       vfs objects = nfs4acl_xattr xattr_tdb
+       nfs4:mode = simple
+       nfs4acl_xattr:encoding = nfs
+       nfs4acl_xattr:version = 40
+       nfs4acl_xattr:xattr_name = security.nfs4acl_xdr
+
+[nfs4acl_nfs_41]
+       path = $shrdir
+       comment = smb username is [%U]
+       vfs objects = nfs4acl_xattr xattr_tdb
+       nfs4:mode = simple
+       nfs4acl_xattr:encoding = nfs
+       nfs4acl_xattr:version = 41
+       nfs4acl_xattr:xattr_name = security.nfs4acl_xdr
+
 [xcopy_share]
        path = $shrdir
        comment = smb username is [%U]
@@ -1944,7 +2478,7 @@ sub provision($$$$$$$$$)
        force directory mode = 0
        vfs objects = xattr_tdb streams_depot
 [aio]
-       copy = tmp
+       copy = durable
        aio read size = 1
        aio write size = 1
 
@@ -2000,6 +2534,31 @@ sub provision($$$$$$$$$)
        fruit:time machine = yes
        fruit:time machine max size = 32K
 
+[vfs_fruit_wipe_intentionally_left_blank_rfork]
+       path = $shrdir
+       vfs objects = fruit streams_xattr acl_xattr xattr_tdb
+       fruit:resource = file
+       fruit:metadata = stream
+       fruit:wipe_intentionally_left_blank_rfork = true
+       fruit:delete_empty_adfiles = false
+       fruit:veto_appledouble = no
+
+[vfs_fruit_delete_empty_adfiles]
+       path = $shrdir
+       vfs objects = fruit streams_xattr acl_xattr xattr_tdb
+       fruit:resource = file
+       fruit:metadata = stream
+       fruit:wipe_intentionally_left_blank_rfork = true
+       fruit:delete_empty_adfiles = true
+       fruit:veto_appledouble = no
+
+[vfs_fruit_zero_fileid]
+       path = $shrdir
+       vfs objects = fruit streams_xattr acl_xattr xattr_tdb
+       fruit:resource = file
+       fruit:metadata = stream
+       fruit:zero_file_id=yes
+
 [badname-tmp]
        path = $badnames_shrdir
        guest ok = yes
@@ -2018,14 +2577,14 @@ sub provision($$$$$$$$$)
        guest ok = yes
 
 [fsrvp_share]
-       path = $shrdir
+       path = $fsrvp_shrdir
        comment = fake shapshots using rsync
        vfs objects = shell_snap shadow_copy2
        shell_snap:check path command = $fake_snap_pl --check
        shell_snap:create command = $fake_snap_pl --create
        shell_snap:delete command = $fake_snap_pl --delete
        # a relative path here fails, the snapshot dir is no longer found
-       shadow:snapdir = $shrdir/.snapshots
+       shadow:snapdir = $fsrvp_shrdir/.snapshots
 
 [shadow1]
        path = $shadow_shrdir
@@ -2159,6 +2718,15 @@ sub provision($$$$$$$$$)
        vfs objects = shadow_copy2
        shadow:mountpoint = $shadow_mntdir
        wide links = yes
+
+[shadow_write]
+       path = $shadow_tstdir
+       comment = previous versions snapshots under mount point
+       vfs objects = shadow_copy2 streams_xattr error_inject
+       aio write size = 0
+       error_inject:pwrite = EBADF
+       shadow:mountpoint = $shadow_tstdir
+
 [dfq]
        path = $shrdir/dfree
        vfs objects = acl_xattr fake_acls xattr_tdb fake_dfq
@@ -2217,26 +2785,76 @@ sub provision($$$$$$$$$)
 [error_inject]
        copy = tmp
        vfs objects = error_inject
-       include = $libdir/error_inject.conf
+       include = $errorinjectconf
+
+[delay_inject]
+       copy = tmp
+       vfs objects = delay_inject
+       kernel share modes = no
+       kernel oplocks = no
+       posix locking = no
+       include = $delayinjectconf
+
+[aio_delay_inject]
+       copy = tmp
+       vfs objects = delay_inject
+       delay_inject:pread_send = 2000
+       delay_inject:pwrite_send = 2000
+
+[brl_delay_inject1]
+       copy = tmp
+       vfs objects = delay_inject
+       delay_inject:brl_lock_windows = 90
+       delay_inject:brl_lock_windows_use_timer = yes
+
+[brl_delay_inject2]
+       copy = tmp
+       vfs objects = delay_inject
+       delay_inject:brl_lock_windows = 90
+       delay_inject:brl_lock_windows_use_timer = no
+
+[delete_readonly]
+       path = $prefix_abs/share
+       delete readonly = yes
        ";
        close(CONF);
 
        my $net = Samba::bindir_path($self, "net");
        my $cmd = "";
+       $cmd .= "UID_WRAPPER_ROOT=1 ";
        $cmd .= "SMB_CONF_PATH=\"$conffile\" ";
        $cmd .= "$net setlocalsid $samsid";
 
-       if (system($cmd) != 0) {
-           warn("Join failed\n$cmd");
+       my $net_ret = system($cmd);
+       if ($net_ret != 0) {
+           warn("net setlocalsid failed: $net_ret\n$cmd");
            return undef;
        }
 
+       unless (open(ERRORCONF, ">$errorinjectconf")) {
+               warn("Unable to open $errorinjectconf");
+               return undef;
+       }
+       close(ERRORCONF);
+
+       unless (open(DELAYCONF, ">$delayinjectconf")) {
+               warn("Unable to open $delayinjectconf");
+               return undef;
+       }
+       close(DELAYCONF);
+
        unless (open(DFQCONF, ">$dfqconffile")) {
                warn("Unable to open $dfqconffile");
                return undef;
        }
        close(DFQCONF);
 
+       unless (open(DELAYCONF, ">$globalinjectconf")) {
+               warn("Unable to open $globalinjectconf");
+               return undef;
+       }
+       close(DELAYCONF);
+
        ##
        ## create a test account
        ##
@@ -2255,6 +2873,9 @@ force_user:x:$uid_force_user:$gid_force_user:force user gecos:$prefix_abs:/bin/f
 smbget_user:x:$uid_smbget:$gid_domusers:smbget_user gecos:$prefix_abs:/bin/false
 user1:x:$uid_user1:$gid_nogroup:user1 gecos:$prefix_abs:/bin/false
 user2:x:$uid_user2:$gid_nogroup:user2 gecos:$prefix_abs:/bin/false
+gooduser:x:$uid_gooduser:$gid_domusers:gooduser gecos:$prefix_abs:/bin/false
+eviluser:x:$uid_eviluser:$gid_domusers:eviluser gecos::/bin/false
+slashuser:x:$uid_slashuser:$gid_domusers:slashuser gecos:/:/bin/false
 ";
        if ($unix_uid != 0) {
                print PASSWD "root:x:$uid_root:$gid_root:root gecos:$prefix_abs:/bin/false
@@ -2292,23 +2913,7 @@ force_user:x:$gid_force_user:
        print HOSTS "${server_ipv6} ${hostname}.samba.example.com ${hostname}\n";
        close(HOSTS);
 
-       ## hosts
-       unless (open(RESOLV_CONF, ">$resolv_conf")) {
-               warn("Unable to open $resolv_conf");
-               return undef;
-       }
-       if (defined($dc_server_ip) or defined($dc_server_ipv6)) {
-               if (defined($dc_server_ip)) {
-                       print RESOLV_CONF "nameserver $dc_server_ip\n";
-               }
-               if (defined($dc_server_ipv6)) {
-                       print RESOLV_CONF "nameserver $dc_server_ipv6\n";
-               }
-       } else {
-               print RESOLV_CONF "nameserver ${server_ip}\n";
-               print RESOLV_CONF "nameserver ${server_ipv6}\n";
-       }
-       close(RESOLV_CONF);
+       $resolv_conf = "$privatedir/no_resolv.conf" unless defined($resolv_conf);
 
        foreach my $evlog (@eventlog_list) {
                my $evlogtdb = "$eventlogdir/$evlog.tdb";
@@ -2325,12 +2930,16 @@ force_user:x:$gid_force_user:
        } else {
                $createuser_env{RESOLV_WRAPPER_CONF} = $resolv_conf;
        }
+       $createuser_env{RESOLV_CONF} = $resolv_conf;
 
        createuser($self, $unix_name, $password, $conffile, \%createuser_env) || die("Unable to create user");
        createuser($self, "force_user", $password, $conffile, \%createuser_env) || die("Unable to create force_user");
        createuser($self, "smbget_user", $password, $conffile, \%createuser_env) || die("Unable to create smbget_user");
        createuser($self, "user1", $password, $conffile, \%createuser_env) || die("Unable to create user1");
        createuser($self, "user2", $password, $conffile, \%createuser_env) || die("Unable to create user2");
+       createuser($self, "gooduser", $password, $conffile, \%createuser_env) || die("Unable to create gooduser");
+       createuser($self, "eviluser", $password, $conffile, \%createuser_env) || die("Unable to create eviluser");
+       createuser($self, "slashuser", $password, $conffile, \%createuser_env) || die("Unable to create slashuser");
 
        open(DNS_UPDATE_LIST, ">$prefix/dns_update_list") or die("Unable to open $$prefix/dns_update_list");
        print DNS_UPDATE_LIST "A $server. $server_ip\n";
@@ -2348,6 +2957,7 @@ force_user:x:$gid_force_user:
        $ret{SMBD_TEST_LOG} = "$prefix/smbd_test.log";
        $ret{SMBD_TEST_LOG_POS} = 0;
        $ret{SERVERCONFFILE} = $conffile;
+       $ret{TESTENV_DIR} = $prefix_abs;
        $ret{CONFIGURATION} ="-s $conffile";
        $ret{LOCK_DIR} = $lockdir;
        $ret{SERVER} = $server;
@@ -2372,6 +2982,7 @@ force_user:x:$gid_force_user:
        } else {
                $ret{RESOLV_WRAPPER_CONF} = $resolv_conf;
        }
+       $ret{RESOLV_CONF} = $resolv_conf;
        $ret{LOCAL_PATH} = "$shrdir";
         $ret{LOGDIR} = $logdir;
 
@@ -2412,7 +3023,7 @@ sub wait_for_start($$$$$)
                        } else {
                                system("$nmblookup $envvars->{CONFIGURATION} -U $envvars->{SERVER_IP} __SAMBA__");
                                system("$nmblookup $envvars->{CONFIGURATION} __SAMBA__");
-                               system("$nmblookup $envvars->{CONFIGURATION} -U 127.255.255.255 __SAMBA__");
+                               system("$nmblookup $envvars->{CONFIGURATION} -U 10.255.255.255 __SAMBA__");
                                system("$nmblookup $envvars->{CONFIGURATION} -U $envvars->{SERVER_IP} $envvars->{SERVER}");
                        }
                        $count++;
@@ -2433,8 +3044,8 @@ sub wait_for_start($$$$$)
            $cmd .= Samba::bindir_path($self, "wbinfo") . " --ping-dc";
 
            do {
+               $ret = system($cmd);
                if ($ret != 0) {
-                   $ret = system($cmd);
                    sleep(1);
                }
                $count++;
@@ -2452,7 +3063,23 @@ sub wait_for_start($$$$$)
 
            my $count = 0;
            do {
-               $ret = system(Samba::bindir_path($self, "smbclient") ." $envvars->{CONFIGURATION} -L $envvars->{SERVER} -U% -p 139");
+               if (defined($envvars->{GNUTLS_FORCE_FIPS_MODE})) {
+                       # We don't have NTLM in FIPS mode, so lets use
+                       # smbcontrol instead of smbclient.
+                       $cmd = Samba::bindir_path($self, "smbcontrol");
+                       $cmd .= " $envvars->{CONFIGURATION}";
+                       $cmd .= " smbd ping";
+               } else {
+                       # This uses NTLM which is not available in FIPS
+                       $cmd = Samba::bindir_path($self, "smbclient");
+                       $cmd .= " $envvars->{CONFIGURATION}";
+                       $cmd .= " -L $envvars->{SERVER}";
+                       $cmd .= " -U%";
+                       $cmd .= " -I $envvars->{SERVER_IP}";
+                       $cmd .= " -p 139";
+               }
+
+               $ret = system($cmd);
                if ($ret != 0) {
                    sleep(1);
                }
@@ -2468,6 +3095,7 @@ sub wait_for_start($$$$$)
        # Ensure we have domain users mapped.
        $netcmd = "NSS_WRAPPER_PASSWD='$envvars->{NSS_WRAPPER_PASSWD}' ";
        $netcmd .= "NSS_WRAPPER_GROUP='$envvars->{NSS_WRAPPER_GROUP}' ";
+       $netcmd .= "UID_WRAPPER_ROOT='1' ";
        $netcmd .= Samba::bindir_path($self, "net") ." $envvars->{CONFIGURATION} ";
 
        $cmd = $netcmd . "groupmap delete ntgroup=domusers";
@@ -2555,4 +3183,252 @@ sub wait_for_start($$$$$)
        return 1;
 }
 
+##
+## provision and start of ctdb
+##
+sub setup_ctdb($$)
+{
+       my ($self, $prefix) = @_;
+       my $num_nodes = 3;
+
+       my $data = $self->provision_ctdb($prefix, $num_nodes);
+       $data or return undef;
+
+       my $rc = $self->check_or_start_ctdb($data);
+       if (not $rc) {
+               print("check_or_start_ctdb() failed\n");
+               return undef;
+       }
+
+       $rc = $self->wait_for_start_ctdb($data);
+       if (not $rc) {
+               print "Cluster startup failed\n";
+               return undef;
+       }
+
+       return $data;
+}
+
+sub provision_ctdb($$$$)
+{
+       my ($self, $prefix, $num_nodes, $no_delete_prefix) = @_;
+       my $rc;
+
+       print "PROVISIONING CTDB...\n";
+
+       my $prefix_abs = abs_path($prefix);
+
+       #
+       # check / create directories:
+       #
+       die ("prefix_abs = ''") if $prefix_abs eq "";
+       die ("prefix_abs = '/'") if $prefix_abs eq "/";
+
+       mkdir ($prefix_abs, 0777);
+
+       print "CREATE CTDB TEST ENVIRONMENT in '$prefix_abs'...\n";
+
+       if (not defined($no_delete_prefix) or not $no_delete_prefix) {
+               system("rm -rf $prefix_abs/*");
+       }
+
+       #
+       # Per-node data
+       #
+       my @nodes = ();
+       for (my $i = 0; $i < $num_nodes; $i++) {
+               my %node = ();
+               my $server_name = "ctdb${i}";
+               my $pub_iface = Samba::get_interface($server_name);
+               my $ip = Samba::get_ipv4_addr($server_name);
+
+               $node{NODE_NUMBER} = "$i";
+               $node{SERVER_NAME} = "$server_name";
+               $node{SOCKET_WRAPPER_DEFAULT_IFACE} = "$pub_iface";
+               $node{IP} = "$ip";
+
+               push(@nodes, \%node);
+       }
+
+       #
+       # nodes
+       #
+       my $nodes_file = "$prefix/nodes.in";
+       unless (open(NODES, ">$nodes_file")) {
+               warn("Unable to open nodesfile '$nodes_file'");
+               return undef;
+       }
+       for (my $i = 0; $i < $num_nodes; $i++) {
+               my $ip = $nodes[$i]->{IP};
+               print NODES "${ip}\n";
+       }
+       close(NODES);
+
+       #
+       # local_daemons.sh setup
+       #
+       # Socket wrapper setup is done by selftest.pl, so don't use
+       # the CTDB-specific setup
+       #
+       my $cmd;
+       $cmd .= "ctdb/tests/local_daemons.sh " . $prefix_abs . " setup";
+       $cmd .= " -n " . $num_nodes;
+       $cmd .= " -N " . $nodes_file;
+       # CTDB should not attempt to manage public addresses -
+       # clients should just connect to CTDB private addresses
+       $cmd .= " -P " . "/dev/null";
+
+       my $ret = system($cmd);
+       if ($ret != 0) {
+               print("\"$cmd\" failed\n");
+               return undef;
+       }
+
+       #
+       # Unix domain socket and node directory for each daemon
+       #
+       for (my $i = 0; $i < $num_nodes; $i++) {
+               my ($cmd, $ret, $out);
+
+               my $cmd_prefix = "ctdb/tests/local_daemons.sh ${prefix_abs}";
+
+               #
+               # socket
+               #
+
+               $cmd = "${cmd_prefix} print-socket ${i}";
+
+               $out = `$cmd`;
+               $ret = $?;
+               if ($ret != 0) {
+                   print("\"$cmd\" failed\n");
+                   return undef;
+               }
+               chomp $out;
+               $nodes[$i]->{SOCKET_FILE} = "$out";
+
+               #
+               # node directory
+               #
+
+               $cmd = "${cmd_prefix} onnode ${i} 'echo \$CTDB_BASE'";
+
+               $out = `$cmd`;
+               $ret = $?;
+               if ($ret != 0) {
+                   print("\"$cmd\" failed\n");
+                   return undef;
+               }
+               chomp $out;
+               $nodes[$i]->{NODE_PREFIX} = "$out";
+       }
+
+       my %ret = ();
+
+       $ret{CTDB_PREFIX} = "$prefix";
+       $ret{NUM_NODES} = $num_nodes;
+       $ret{CTDB_NODES} = \@nodes;
+       $ret{CTDB_NODES_FILE} = $nodes_file;
+
+       return \%ret;
+}
+
+sub check_or_start_ctdb($$) {
+       my ($self, $data) = @_;
+
+       my $prefix = $data->{CTDB_PREFIX};
+       my $num_nodes = $data->{NUM_NODES};
+       my $nodes = $data->{CTDB_NODES};
+       my $STDIN_READER;
+
+       # Share a single stdin pipe for all nodes
+       pipe($STDIN_READER, $data->{CTDB_STDIN_PIPE});
+
+       for (my $i = 0; $i < $num_nodes; $i++) {
+               my $node = $nodes->[$i];
+
+               $node->{STDIN_PIPE} = $data->{CTDB_STDIN_PIPE};
+
+               my $cmd = "ctdb/tests/local_daemons.sh";
+               my @full_cmd = ("$cmd", "$prefix", "start", "$i");
+               # Dummy environment variables to avoid
+               # Samba3::get_env_for_process() from generating them
+               # and including UID_WRAPPER_ROOT=1, which causes
+               # "Unable to secure ctdb socket" error.
+               my $env_vars = {
+                       CTDB_DUMMY => "1",
+               };
+               my $daemon_ctx = {
+                       NAME => "ctdbd",
+                       BINARY_PATH => $cmd,
+                       FULL_CMD => [ @full_cmd ],
+                       TEE_STDOUT => 1,
+                       LOG_FILE => "/dev/null",
+                       ENV_VARS => $env_vars,
+               };
+
+               print "STARTING CTDBD (node ${i})\n";
+
+               # This does magic with $STDIN_READER, so use it
+               my $ret = Samba::fork_and_exec($self,
+                                              $node,
+                                              $daemon_ctx,
+                                              $STDIN_READER);
+
+               if ($ret == 0) {
+                       print("\"$cmd\" failed\n");
+                       teardown_env_ctdb($self, $data);
+                       return 0;
+               }
+       }
+
+       close($STDIN_READER);
+
+       return 1;
+}
+
+sub wait_for_start_ctdb($$)
+{
+       my ($self, $data) = @_;
+
+       my $prefix = $data->{CTDB_PREFIX};
+
+       print "Wait for ctdbd...\n";
+
+       my $ctdb = Samba::bindir_path($self, "ctdb");
+       my $cmd;
+       $cmd .= "ctdb/tests/local_daemons.sh ${prefix} onnode all";
+       $cmd .= " ${ctdb} nodestatus all 2>&1";
+
+       my $count = 0;
+       my $wait_seconds = 60;
+       my $out;
+
+       until ($count > $wait_seconds) {
+               $out = `$cmd`;
+               my $ret = $?;
+               if ($ret == 0) {
+                       print "\ncluster became healthy\n";
+                       last;
+               }
+               print "Waiting for CTDB...\n";
+               sleep(1);
+               $count++;
+       }
+
+       if ($count > $wait_seconds) {
+               print "\nGiving up to wait for CTDB...\n";
+               print "${out}\n\n";
+               print "CTDB log:\n";
+               $cmd = "ctdb/tests/local_daemons.sh ${prefix} print-log all >&2";
+               system($cmd);
+               teardown_env_ctdb($self, $data);
+               return 0;
+       }
+
+       print "\nCTDB initialized\n";
+
+       return 1;
+}
+
 1;