vfs_catia: Fix return value in lock functions
[bbaumbach/samba-autobuild/.git] / source3 / torture / test_smb2.c
index bc338496b7b53a1efdcf7895cbde68ca6a1944f3..0cfb68cbbef3836c73feccebafcc8b951372770c 100644 (file)
 #include "../libcli/smb/smbXcli_base.h"
 #include "libcli/security/security.h"
 #include "libsmb/proto.h"
+#include "auth/credentials/credentials.h"
 #include "auth/gensec/gensec.h"
 #include "auth_generic.h"
 #include "../librpc/ndr/libndr.h"
+#include "libsmb/clirap.h"
 
 extern fstring host, workgroup, share, password, username, myname;
 extern struct cli_credentials *torture_creds;
@@ -64,7 +66,7 @@ bool run_smb2_basic(int dummy)
                return false;
        }
 
-       status = cli_tree_connect(cli, share, "?????", "", 0);
+       status = cli_tree_connect(cli, share, "?????", NULL);
        if (!NT_STATUS_IS_OK(status)) {
                printf("cli_tree_connect returned %s\n", nt_errstr(status));
                return false;
@@ -169,7 +171,10 @@ bool run_smb2_basic(int dummy)
        }
 
        saved_tid = smb2cli_tcon_current_id(cli->smb2.tcon);
-       saved_tcon = cli->smb2.tcon;
+       saved_tcon = cli_state_save_tcon(cli);
+       if (saved_tcon == NULL) {
+               return false;
+       }
        cli->smb2.tcon = smbXcli_tcon_create(cli);
        smb2cli_tcon_set_values(cli->smb2.tcon,
                                NULL, /* session */
@@ -186,8 +191,7 @@ bool run_smb2_basic(int dummy)
                printf("smb2cli_tdis returned %s\n", nt_errstr(status));
                return false;
        }
-       talloc_free(cli->smb2.tcon);
-       cli->smb2.tcon = saved_tcon;
+       cli_state_restore_tcon(cli, saved_tcon);
 
        status = smb2cli_tdis(cli->conn,
                              cli->timeout,
@@ -243,40 +247,12 @@ bool run_smb2_negprot(int dummy)
        }
 
        protocol = smbXcli_conn_protocol(cli->conn);
+       name = smb_protocol_types_string(protocol);
 
-       switch (protocol) {
-       case PROTOCOL_SMB2_02:
-               name = "SMB2_02";
-               break;
-       case PROTOCOL_SMB2_10:
-               name = "SMB2_10";
-               break;
-       case PROTOCOL_SMB2_22:
-               name = "SMB2_22";
-               break;
-       case PROTOCOL_SMB2_24:
-               name = "SMB2_24";
-               break;
-       case PROTOCOL_SMB3_00:
-               name = "SMB3_00";
-               break;
-       case PROTOCOL_SMB3_02:
-               name = "SMB3_02";
-               break;
-       case PROTOCOL_SMB3_10:
-               name = "SMB3_10";
-               break;
-       case PROTOCOL_SMB3_11:
-               name = "SMB3_11";
-               break;
-       default:
-               break;
-       }
-
-       if (name) {
+       if (protocol >= PROTOCOL_SMB2_02) {
                printf("Server supports %s\n", name);
        } else {
-               printf("Server DOES NOT support SMB2\n");
+               printf("Server DOES NOT support SMB2, only %s\n", name);
                return false;
        }
 
@@ -299,6 +275,47 @@ bool run_smb2_negprot(int dummy)
        return true;
 }
 
+bool run_smb2_anonymous(int dummy)
+{
+       struct cli_state *cli = NULL;
+       NTSTATUS status;
+       struct cli_credentials *anon_creds = NULL;
+       bool guest = false;
+
+       printf("Starting SMB2-ANONYMOUS\n");
+
+       if (!torture_init_connection(&cli)) {
+               return false;
+       }
+
+       status = smbXcli_negprot(cli->conn, cli->timeout,
+                                PROTOCOL_SMB2_02, PROTOCOL_LATEST);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+               return false;
+       }
+
+       anon_creds = cli_credentials_init_anon(talloc_tos());
+       if (anon_creds == NULL) {
+               printf("cli_credentials_init_anon failed\n");
+               return false;
+       }
+
+       status = cli_session_setup_creds(cli, anon_creds);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("cli_session_setup returned %s\n", nt_errstr(status));
+               return false;
+       }
+
+       guest = smbXcli_session_is_guest(cli->smb2.session);
+       if (guest) {
+               printf("anonymous session should not have guest authentication\n");
+               return false;
+       }
+
+       return true;
+}
+
 bool run_smb2_session_reconnect(int dummy)
 {
        struct cli_state *cli1;
@@ -336,7 +353,7 @@ bool run_smb2_session_reconnect(int dummy)
                return false;
        }
 
-       status = cli_tree_connect(cli1, share, "?????", "", 0);
+       status = cli_tree_connect(cli1, share, "?????", NULL);
        if (!NT_STATUS_IS_OK(status)) {
                printf("cli_tree_connect returned %s\n", nt_errstr(status));
                return false;
@@ -417,21 +434,10 @@ bool run_smb2_session_reconnect(int dummy)
 
        gensec_want_feature(auth_generic_state->gensec_security,
                            GENSEC_FEATURE_SESSION_KEY);
-       status = auth_generic_set_username(auth_generic_state, username);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_username returned %s\n", nt_errstr(status));
-               return false;
-       }
-
-       status = auth_generic_set_domain(auth_generic_state, workgroup);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_domain returned %s\n", nt_errstr(status));
-               return false;
-       }
 
-       status = auth_generic_set_password(auth_generic_state, password);
+       status = auth_generic_set_creds(auth_generic_state, torture_creds);
        if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_password returned %s\n", nt_errstr(status));
+               printf("auth_generic_set_creds returned %s\n", nt_errstr(status));
                return false;
        }
 
@@ -538,7 +544,7 @@ bool run_smb2_session_reconnect(int dummy)
                return false;
        }
 
-       status = cli_tree_connect(cli1, share, "?????", "", 0);
+       status = cli_tree_connect(cli1, share, "?????", NULL);
        if (!NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED)) {
                printf("cli_tree_connect returned %s\n", nt_errstr(status));
                return false;
@@ -662,7 +668,7 @@ bool run_smb2_session_reconnect(int dummy)
 
        /* now do a new tcon and test file calls again */
 
-       status = cli_tree_connect(cli2, share, "?????", "", 0);
+       status = cli_tree_connect(cli2, share, "?????", NULL);
        if (!NT_STATUS_IS_OK(status)) {
                printf("cli_tree_connect returned %s\n", nt_errstr(status));
                return false;
@@ -755,7 +761,7 @@ bool run_smb2_tcon_dependence(int dummy)
                return false;
        }
 
-       status = cli_tree_connect(cli, share, "?????", "", 0);
+       status = cli_tree_connect(cli, share, "?????", NULL);
        if (!NT_STATUS_IS_OK(status)) {
                printf("cli_tree_connect returned %s\n", nt_errstr(status));
                return false;
@@ -907,7 +913,7 @@ bool run_smb2_multi_channel(int dummy)
                return false;
        }
 
-       status = cli_tree_connect(cli1, share, "?????", "", 0);
+       status = cli_tree_connect(cli1, share, "?????", NULL);
        if (!NT_STATUS_IS_OK(status)) {
                printf("cli_tree_connect returned %s\n", nt_errstr(status));
                return false;
@@ -931,21 +937,10 @@ bool run_smb2_multi_channel(int dummy)
 
        gensec_want_feature(auth_generic_state->gensec_security,
                            GENSEC_FEATURE_SESSION_KEY);
-       status = auth_generic_set_username(auth_generic_state, username);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_username returned %s\n", nt_errstr(status));
-               return false;
-       }
-
-       status = auth_generic_set_domain(auth_generic_state, workgroup);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_domain returned %s\n", nt_errstr(status));
-               return false;
-       }
 
-       status = auth_generic_set_password(auth_generic_state, password);
+       status = auth_generic_set_creds(auth_generic_state, torture_creds);
        if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_password returned %s\n", nt_errstr(status));
+               printf("auth_generic_set_creds returned %s\n", nt_errstr(status));
                return false;
        }
 
@@ -1065,21 +1060,10 @@ bool run_smb2_multi_channel(int dummy)
 
        gensec_want_feature(auth_generic_state->gensec_security,
                            GENSEC_FEATURE_SESSION_KEY);
-       status = auth_generic_set_username(auth_generic_state, username);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_username returned %s\n", nt_errstr(status));
-               return false;
-       }
 
-       status = auth_generic_set_domain(auth_generic_state, workgroup);
+       status = auth_generic_set_creds(auth_generic_state, torture_creds);
        if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_domain returned %s\n", nt_errstr(status));
-               return false;
-       }
-
-       status = auth_generic_set_password(auth_generic_state, password);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_password returned %s\n", nt_errstr(status));
+               printf("auth_generic_set_creds returned %s\n", nt_errstr(status));
                return false;
        }
 
@@ -1251,21 +1235,10 @@ bool run_smb2_multi_channel(int dummy)
 
        gensec_want_feature(auth_generic_state->gensec_security,
                            GENSEC_FEATURE_SESSION_KEY);
-       status = auth_generic_set_username(auth_generic_state, username);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_username returned %s\n", nt_errstr(status));
-               return false;
-       }
-
-       status = auth_generic_set_domain(auth_generic_state, workgroup);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_domain returned %s\n", nt_errstr(status));
-               return false;
-       }
 
-       status = auth_generic_set_password(auth_generic_state, password);
+       status = auth_generic_set_creds(auth_generic_state, torture_creds);
        if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_password returned %s\n", nt_errstr(status));
+               printf("auth_generic_set_creds returned %s\n", nt_errstr(status));
                return false;
        }
 
@@ -1497,7 +1470,7 @@ bool run_smb2_session_reauth(int dummy)
                return false;
        }
 
-       status = cli_tree_connect(cli, share, "?????", "", 0);
+       status = cli_tree_connect(cli, share, "?????", NULL);
        if (!NT_STATUS_IS_OK(status)) {
                printf("cli_tree_connect returned %s\n", nt_errstr(status));
                return false;
@@ -1559,21 +1532,10 @@ bool run_smb2_session_reauth(int dummy)
 
        gensec_want_feature(auth_generic_state->gensec_security,
                            GENSEC_FEATURE_SESSION_KEY);
-       status = auth_generic_set_username(auth_generic_state, username);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_username returned %s\n", nt_errstr(status));
-               return false;
-       }
-
-       status = auth_generic_set_domain(auth_generic_state, workgroup);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_domain returned %s\n", nt_errstr(status));
-               return false;
-       }
 
-       status = auth_generic_set_password(auth_generic_state, password);
+       status = auth_generic_set_creds(auth_generic_state, torture_creds);
        if (!NT_STATUS_IS_OK(status)) {
-               printf("auth_generic_set_password returned %s\n", nt_errstr(status));
+               printf("auth_generic_set_creds returned %s\n", nt_errstr(status));
                return false;
        }
 
@@ -1655,7 +1617,7 @@ bool run_smb2_session_reauth(int dummy)
                                    cli->timeout,
                                    cli->smb2.session,
                                    cli->smb2.tcon,
-                                   SMB2_GETINFO_SECURITY,
+                                   SMB2_0_INFO_SECURITY,
                                    0, /* in_file_info_class */
                                    1024, /* in_max_output_length */
                                    NULL, /* in_input_buffer */
@@ -1675,7 +1637,7 @@ bool run_smb2_session_reauth(int dummy)
                                    cli->timeout,
                                    cli->smb2.session,
                                    cli->smb2.tcon,
-                                   SMB2_GETINFO_FILE,
+                                   SMB2_0_INFO_FILE,
                                    in_file_info_class,
                                    1024, /* in_max_output_length */
                                    NULL, /* in_input_buffer */
@@ -1698,7 +1660,7 @@ bool run_smb2_session_reauth(int dummy)
                                  cli->timeout,
                                  cli->smb2.session,
                                  cli->smb2.tcon,
-                                 SMB2_GETINFO_FILE,
+                                 SMB2_0_INFO_FILE,
                                  in_file_info_class,
                                  &in_input_buffer,
                                  0, /* in_additional_info */
@@ -1757,7 +1719,7 @@ bool run_smb2_session_reauth(int dummy)
                                0, /* flags */
                                0, /* capabilities */
                                0  /* maximal_access */);
-       status = cli_tree_connect(cli, share, "?????", "", 0);
+       status = cli_tree_connect(cli, share, "?????", NULL);
        if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
                printf("cli_tree_connect returned %s\n", nt_errstr(status));
                return false;
@@ -1804,7 +1766,7 @@ bool run_smb2_session_reauth(int dummy)
                                    cli->timeout,
                                    cli->smb2.session,
                                    cli->smb2.tcon,
-                                   SMB2_GETINFO_SECURITY,
+                                   SMB2_0_INFO_SECURITY,
                                    0, /* in_file_info_class */
                                    1024, /* in_max_output_length */
                                    NULL, /* in_input_buffer */
@@ -1824,7 +1786,7 @@ bool run_smb2_session_reauth(int dummy)
                                    cli->timeout,
                                    cli->smb2.session,
                                    cli->smb2.tcon,
-                                   SMB2_GETINFO_FILE,
+                                   SMB2_0_INFO_FILE,
                                    in_file_info_class,
                                    1024, /* in_max_output_length */
                                    NULL, /* in_input_buffer */
@@ -1847,7 +1809,7 @@ bool run_smb2_session_reauth(int dummy)
                                  cli->timeout,
                                  cli->smb2.session,
                                  cli->smb2.tcon,
-                                 SMB2_GETINFO_FILE,
+                                 SMB2_0_INFO_FILE,
                                  in_file_info_class,
                                  &in_input_buffer,
                                  0, /* in_additional_info */
@@ -1863,7 +1825,7 @@ bool run_smb2_session_reauth(int dummy)
                                    cli->timeout,
                                    cli->smb2.session,
                                    cli->smb2.tcon,
-                                   SMB2_GETINFO_FILE,
+                                   SMB2_0_INFO_FILE,
                                    in_file_info_class,
                                    1024, /* in_max_output_length */
                                    NULL, /* in_input_buffer */
@@ -1937,7 +1899,7 @@ bool run_smb2_session_reauth(int dummy)
                                0, /* flags */
                                0, /* capabilities */
                                0  /* maximal_access */);
-       status = cli_tree_connect(cli, share, "?????", "", 0);
+       status = cli_tree_connect(cli, share, "?????", NULL);
        if (!NT_STATUS_IS_OK(status)) {
                printf("cli_tree_connect returned %s\n", nt_errstr(status));
                return false;
@@ -1947,3 +1909,429 @@ bool run_smb2_session_reauth(int dummy)
 
        return true;
 }
+
+static NTSTATUS check_size(struct cli_state *cli,
+                               uint16_t fnum,
+                               const char *fname,
+                               size_t size)
+{
+       off_t size_read = 0;
+
+       NTSTATUS status = cli_qfileinfo_basic(cli,
+                               fnum,
+                               NULL,
+                               &size_read,
+                               NULL,
+                               NULL,
+                               NULL,
+                               NULL,
+                               NULL);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("cli_smb2_qfileinfo_basic of %s failed (%s)\n",
+                       fname,
+                       nt_errstr(status));
+               return status;
+       }
+
+       if (size != size_read) {
+               printf("size (%u) != size_read(%u) for %s\n",
+                       (unsigned int)size,
+                       (unsigned int)size_read,
+                       fname);
+               /* Use EOF to mean bad size. */
+               return NT_STATUS_END_OF_FILE;
+       }
+       return NT_STATUS_OK;
+}
+
+/* Ensure cli_ftruncate() works for SMB2. */
+
+bool run_smb2_ftruncate(int dummy)
+{
+       struct cli_state *cli = NULL;
+       const char *fname = "smb2_ftruncate.txt";
+       uint16_t fnum = (uint16_t)-1;
+       bool correct = false;
+       size_t buflen = 1024*1024;
+       uint8_t *buf = NULL;
+       unsigned int i;
+       NTSTATUS status;
+
+       printf("Starting SMB2-FTRUNCATE\n");
+
+       if (!torture_init_connection(&cli)) {
+               goto fail;
+       }
+
+       status = smbXcli_negprot(cli->conn, cli->timeout,
+                                PROTOCOL_SMB2_02, PROTOCOL_SMB2_02);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+               goto fail;
+       }
+
+       status = cli_session_setup_creds(cli, torture_creds);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("cli_session_setup returned %s\n", nt_errstr(status));
+               goto fail;
+       }
+
+       status = cli_tree_connect(cli, share, "?????", NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("cli_tree_connect returned %s\n", nt_errstr(status));
+               goto fail;
+       }
+
+       cli_setatr(cli, fname, 0, 0);
+       cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+       status = cli_ntcreate(cli,
+                               fname,
+                               0,
+                               GENERIC_ALL_ACCESS,
+                               FILE_ATTRIBUTE_NORMAL,
+                               FILE_SHARE_NONE,
+                               FILE_CREATE,
+                               0,
+                               0,
+                               &fnum,
+                               NULL);
+
+        if (!NT_STATUS_IS_OK(status)) {
+                printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+                goto fail;
+        }
+
+       buf = talloc_zero_array(cli, uint8_t, buflen);
+       if (buf == NULL) {
+               goto fail;
+       }
+
+       /* Write 1MB. */
+       status = cli_writeall(cli,
+                               fnum,
+                               0,
+                               buf,
+                               0,
+                               buflen,
+                               NULL);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("write of %u to %s failed (%s)\n",
+                       (unsigned int)buflen,
+                       fname,
+                       nt_errstr(status));
+               goto fail;
+       }
+
+       status = check_size(cli, fnum, fname, buflen);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       /* Now ftruncate. */
+       for ( i = 0; i < 10; i++) {
+               status = cli_ftruncate(cli, fnum, i*1024);
+               if (!NT_STATUS_IS_OK(status)) {
+                       printf("cli_ftruncate %u of %s failed (%s)\n",
+                               (unsigned int)i*1024,
+                               fname,
+                               nt_errstr(status));
+                       goto fail;
+               }
+               status = check_size(cli, fnum, fname, i*1024);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto fail;
+               }
+       }
+
+       correct = true;
+
+  fail:
+
+       if (cli == NULL) {
+               return false;
+       }
+
+       if (fnum != (uint16_t)-1) {
+               cli_close(cli, fnum);
+       }
+       cli_setatr(cli, fname, 0, 0);
+       cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+       if (!torture_close_connection(cli)) {
+               correct = false;
+       }
+       return correct;
+}
+
+/* Ensure SMB2 flush on directories behaves correctly. */
+
+static bool test_dir_fsync(struct cli_state *cli, const char *path)
+{
+       NTSTATUS status;
+       uint64_t fid_persistent, fid_volatile;
+       uint8_t *dir_data = NULL;
+       uint32_t dir_data_length = 0;
+
+       /* Open directory - no write abilities. */
+       status = smb2cli_create(cli->conn, cli->timeout, cli->smb2.session,
+                       cli->smb2.tcon, path,
+                       SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+                       SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+                       SEC_STD_SYNCHRONIZE|
+                       SEC_DIR_LIST|
+                       SEC_DIR_READ_ATTRIBUTE, /* desired_access, */
+                       0, /* file_attributes, */
+                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+                       FILE_OPEN, /* create_disposition, */
+                       FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE, /* create_options, */
+                       NULL, /* smb2_create_blobs *blobs */
+                       &fid_persistent,
+                       &fid_volatile,
+                       NULL, NULL, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_create '%s' (readonly) returned %s\n",
+                       path,
+                       nt_errstr(status));
+               return false;
+       }
+
+       status = smb2cli_query_directory(
+               cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+               1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+               talloc_tos(), &dir_data, &dir_data_length);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_query_directory returned %s\n",
+                       nt_errstr(status));
+               return false;
+       }
+
+       /* Open directory no write access. Flush should fail. */
+
+       status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+                              cli->smb2.tcon, fid_persistent, fid_volatile);
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+               printf("smb2cli_flush on a read-only directory returned %s\n",
+                       nt_errstr(status));
+               return false;
+       }
+
+       status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+                              cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_close returned %s\n", nt_errstr(status));
+               return false;
+       }
+
+       /* Open directory write-attributes only. Flush should still fail. */
+
+       status = smb2cli_create(cli->conn, cli->timeout, cli->smb2.session,
+                       cli->smb2.tcon, path,
+                       SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+                       SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+                       SEC_STD_SYNCHRONIZE|
+                       SEC_DIR_LIST|
+                       SEC_DIR_WRITE_ATTRIBUTE|
+                       SEC_DIR_READ_ATTRIBUTE, /* desired_access, */
+                       0, /* file_attributes, */
+                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+                       FILE_OPEN, /* create_disposition, */
+                       FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE, /* create_options, */
+                       NULL, /* smb2_create_blobs *blobs */
+                       &fid_persistent,
+                       &fid_volatile,
+                       NULL, NULL, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_create '%s' (write attr) returned %s\n",
+                       path,
+                       nt_errstr(status));
+               return false;
+       }
+
+       status = smb2cli_query_directory(
+               cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+               1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+               talloc_tos(), &dir_data, &dir_data_length);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+               return false;
+       }
+
+       status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+                              cli->smb2.tcon, fid_persistent, fid_volatile);
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+               printf("smb2cli_flush on a write-attributes directory "
+                       "returned %s\n",
+                       nt_errstr(status));
+               return false;
+       }
+
+       status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+                              cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_close returned %s\n", nt_errstr(status));
+               return false;
+       }
+
+       /* Open directory with SEC_DIR_ADD_FILE access. Flush should now succeed. */
+
+       status = smb2cli_create(cli->conn, cli->timeout, cli->smb2.session,
+                       cli->smb2.tcon, path,
+                       SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+                       SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+                       SEC_STD_SYNCHRONIZE|
+                       SEC_DIR_LIST|
+                       SEC_DIR_ADD_FILE, /* desired_access, */
+                       0, /* file_attributes, */
+                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+                       FILE_OPEN, /* create_disposition, */
+                       FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE, /* create_options, */
+                       NULL, /* smb2_create_blobs *blobs */
+                       &fid_persistent,
+                       &fid_volatile,
+                       NULL, NULL, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_create '%s' (write FILE access) returned %s\n",
+                       path,
+                       nt_errstr(status));
+               return false;
+       }
+
+       status = smb2cli_query_directory(
+               cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+               1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+               talloc_tos(), &dir_data, &dir_data_length);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+               return false;
+       }
+
+       status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+                              cli->smb2.tcon, fid_persistent, fid_volatile);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_flush on a directory returned %s\n",
+                       nt_errstr(status));
+               return false;
+       }
+
+       status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+                              cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_close returned %s\n", nt_errstr(status));
+               return false;
+       }
+
+       /* Open directory with SEC_DIR_ADD_FILE access. Flush should now succeed. */
+
+       status = smb2cli_create(cli->conn, cli->timeout, cli->smb2.session,
+                       cli->smb2.tcon, path,
+                       SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+                       SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+                       SEC_STD_SYNCHRONIZE|
+                       SEC_DIR_LIST|
+                       SEC_DIR_ADD_SUBDIR, /* desired_access, */
+                       0, /* file_attributes, */
+                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+                       FILE_OPEN, /* create_disposition, */
+                       FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE, /* create_options, */
+                       NULL, /* smb2_create_blobs *blobs */
+                       &fid_persistent,
+                       &fid_volatile,
+                       NULL, NULL, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_create '%s' (write DIR access) returned %s\n",
+                       path,
+                       nt_errstr(status));
+               return false;
+       }
+
+       status = smb2cli_query_directory(
+               cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+               1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+               talloc_tos(), &dir_data, &dir_data_length);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+               return false;
+       }
+
+       status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+                              cli->smb2.tcon, fid_persistent, fid_volatile);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_flush on a directory returned %s\n",
+                       nt_errstr(status));
+               return false;
+       }
+
+       status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+                              cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smb2cli_close returned %s\n", nt_errstr(status));
+               return false;
+       }
+
+
+       return true;
+}
+
+bool run_smb2_dir_fsync(int dummy)
+{
+       struct cli_state *cli = NULL;
+       NTSTATUS status;
+       bool bret = false;
+       const char *dname = "fsync_test_dir";
+
+       printf("Starting SMB2-DIR-FSYNC\n");
+
+       if (!torture_init_connection(&cli)) {
+               return false;
+       }
+
+       status = smbXcli_negprot(cli->conn, cli->timeout,
+                                PROTOCOL_SMB2_02, PROTOCOL_SMB2_02);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+               return false;
+       }
+
+       status = cli_session_setup_creds(cli, torture_creds);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("cli_session_setup returned %s\n", nt_errstr(status));
+               return false;
+       }
+
+       status = cli_tree_connect(cli, share, "?????", NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("cli_tree_connect returned %s\n", nt_errstr(status));
+               return false;
+       }
+
+       (void)cli_rmdir(cli, dname);
+       status = cli_mkdir(cli, dname);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("cli_mkdir(%s) returned %s\n",
+                       dname,
+                       nt_errstr(status));
+               return false;
+       }
+
+       /* Test on a subdirectory. */
+       bret = test_dir_fsync(cli, dname);
+       if (bret == false) {
+               (void)cli_rmdir(cli, dname);
+               return false;
+       }
+       (void)cli_rmdir(cli, dname);
+
+       /* Test on the root handle of a share. */
+       bret = test_dir_fsync(cli, "");
+       if (bret == false) {
+               return false;
+       }
+       return true;
+}