Added torture test for doing an openX over a directory
[kai/samba.git] / source4 / torture / raw / open.c
index d28a8bd14eb562726fddec07c7736b6f97ebe495..9f35aae4d0a87de2a9a5152e583a61a67408d9eb 100644 (file)
@@ -29,7 +29,6 @@
 #include "torture/util.h"
 #include "auth/credentials/credentials.h"
 #include "lib/cmdline/popt_common.h"
-#include "param/param.h"
 
 /* enum for whether reads/writes are possible on a file */
 enum rdwr_mode {RDWR_NONE, RDWR_RDONLY, RDWR_WRONLY, RDWR_RDWR};
@@ -844,6 +843,8 @@ static bool test_nttrans_create(struct smbcli_state *cli, struct torture_context
        int fnum = -1;
        bool ret = true;
        int i;
+       uint32_t ok_mask, not_supported_mask, invalid_parameter_mask;
+       uint32_t not_a_directory_mask, unexpected_mask;
        struct {
                uint32_t open_disp;
                bool with_file;
@@ -954,6 +955,93 @@ static bool test_nttrans_create(struct smbcli_state *cli, struct torture_context
        CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
        CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
        smbcli_close(cli->tree, fnum);
+
+       /* check no-recall - don't pull a file from tape on a HSM */
+       io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_NO_RECALL;
+       status = smb_raw_open(cli->tree, tctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.file.fnum;
+
+       CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+       CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED);
+       CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+       CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+       CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+       CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+       CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+       CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+       CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+       CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+       CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+       smbcli_close(cli->tree, fnum);
+
+       /* Check some create options (these all should be ignored) */
+       for (i=0; i < 32; i++) {
+               uint32_t create_option = (1 << i) & NTCREATEX_OPTIONS_MUST_IGNORE_MASK;
+               if (create_option == 0) {
+                       continue;
+               }
+               io.ntcreatex.in.create_options = create_option;
+               status = smb_raw_open(cli->tree, tctx, &io);
+               if (!NT_STATUS_IS_OK(status)) {
+                       printf("ntcreatex create option 0x%08x gave %s - should give NT_STATUS_OK\n",
+                              create_option, nt_errstr(status));
+               }
+               CHECK_STATUS(status, NT_STATUS_OK);
+               fnum = io.ntcreatex.out.file.fnum;
+
+               CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+               CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED);
+               CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+               CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+               CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+               CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+               CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+               CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+               CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+               CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+               CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+               smbcli_close(cli->tree, fnum);
+       }
+
+       io.ntcreatex.in.file_attr = 0;
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+       io.ntcreatex.in.access_mask     = SEC_FLAG_MAXIMUM_ALLOWED;
+
+       /* Check for options that should return NOT_SUPPORTED, OK or INVALID_PARAMETER */
+       ok_mask = 0;
+       not_supported_mask = 0;
+       invalid_parameter_mask = 0;
+       not_a_directory_mask = 0;
+       unexpected_mask = 0;
+       for (i=0; i < 32; i++) {
+               uint32_t create_option = 1<<i;
+               if (create_option & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
+                       continue;
+               }
+               io.ntcreatex.in.create_options = create_option;
+               status = smb_raw_open(cli->tree, tctx, &io);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+                       not_supported_mask |= create_option;
+               } else if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+                       ok_mask |= create_option;
+                       smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+               } else if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+                       invalid_parameter_mask |= create_option;
+               } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
+                       not_a_directory_mask |= 1<<i;
+               } else {
+                       unexpected_mask |= 1<<i;
+                       printf("create option 0x%08x returned %s\n", create_option, nt_errstr(status));
+               }
+       }
+
+       CHECK_VAL(ok_mask,                0x00efcfce);
+       CHECK_VAL(not_a_directory_mask,   0x00000001);
+       CHECK_VAL(not_supported_mask,     0x00002000);
+       CHECK_VAL(invalid_parameter_mask, 0xff100030);
+       CHECK_VAL(unexpected_mask,        0x00000000);
+
        smbcli_unlink(cli->tree, fname);
 
 
@@ -1334,6 +1422,52 @@ done:
        return ret;
 }
 
+/*
+  test RAW_OPEN_OPENX against an existing directory to
+  ensure it returns NT_STATUS_FILE_IS_A_DIRECTORY.
+  Samba 3.2.0 - 3.2.6 are known to fail this.
+  
+*/
+static bool test_openx_over_dir(struct smbcli_state *cli, TALLOC_CTX *tctx)
+{
+       union smb_open io;
+       const char *fname = BASEDIR "\\openx_over_dir";
+       NTSTATUS status;
+       int d_fnum = -1;
+       int fnum = -1;
+       bool ret = true;
+
+       printf("Checking RAW_OPEN_OPENX over an existing directory\n");
+       smbcli_unlink(cli->tree, fname);
+
+        /* Create the Directory */
+       status = create_directory_handle(cli->tree, fname, &d_fnum);
+       smbcli_close(cli->tree, d_fnum);        
+
+        /* Prepare to open the file over the directory. */
+       io.openx.level = RAW_OPEN_OPENX;
+       io.openx.in.fname = fname;
+       io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
+       io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
+       io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN;
+       io.openx.in.search_attrs = 0;
+       io.openx.in.file_attrs = 0;
+       io.openx.in.write_time = 0;
+       io.openx.in.size = 1024*1024;
+       io.openx.in.timeout = 0;
+
+       status = smb_raw_open(cli->tree, tctx, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
+       fnum = io.openx.out.file.fnum;
+
+done:
+       smbcli_close(cli->tree, fnum);
+       smbcli_unlink(cli->tree, fname);
+
+       return ret;
+}
+
+
 /* A little torture test to expose a race condition in Samba 3.0.20 ... :-) */
 
 static bool test_raw_open_multi(struct torture_context *tctx)
@@ -1350,21 +1484,19 @@ static bool test_raw_open_multi(struct torture_context *tctx)
        const char *host = torture_setting_string(tctx, "host", NULL);
        const char *share = torture_setting_string(tctx, "share", NULL);
        int i, num_files = 3;
-       struct event_context *ev;
        int num_ok = 0;
        int num_collision = 0;
        
-       ev = cli_credentials_get_event_context(cmdline_credentials);
        clients = talloc_array(mem_ctx, struct smbcli_state *, num_files);
        requests = talloc_array(mem_ctx, struct smbcli_request *, num_files);
        ios = talloc_array(mem_ctx, union smb_open, num_files);
-       if ((ev == NULL) || (clients == NULL) || (requests == NULL) ||
+       if ((tctx->ev == NULL) || (clients == NULL) || (requests == NULL) ||
            (ios == NULL)) {
                DEBUG(0, ("talloc failed\n"));
                return false;
        }
 
-       if (!torture_open_connection_share(mem_ctx, &cli, tctx, host, share, ev)) {
+       if (!torture_open_connection_share(mem_ctx, &cli, tctx, host, share, tctx->ev)) {
                return false;
        }
 
@@ -1372,7 +1504,7 @@ static bool test_raw_open_multi(struct torture_context *tctx)
 
        for (i=0; i<num_files; i++) {
                if (!torture_open_connection_share(mem_ctx, &(clients[i]),
-                                                  tctx, host, share, ev)) {
+                                                  tctx, host, share, tctx->ev)) {
                        DEBUG(0, ("Could not open %d'th connection\n", i));
                        return false;
                }
@@ -1441,7 +1573,7 @@ static bool test_raw_open_multi(struct torture_context *tctx)
                        break;
                }
 
-               if (event_loop_once(ev) != 0) {
+               if (event_loop_once(tctx->ev) != 0) {
                        DEBUG(0, ("event_loop_once failed\n"));
                        return false;
                }
@@ -1458,6 +1590,64 @@ static bool test_raw_open_multi(struct torture_context *tctx)
        return ret;
 }
 
+/*
+  test opening for delete on a read-only attribute file.
+*/
+static bool test_open_for_delete(struct smbcli_state *cli, struct torture_context *tctx)
+{
+       union smb_open io;
+       union smb_fileinfo finfo;
+       const char *fname = BASEDIR "\\torture_open_for_delete.txt";
+       NTSTATUS status;
+       int fnum = -1;
+       bool ret = true;
+
+       printf("Checking RAW_NTCREATEX for delete on a readonly file.\n");
+
+       /* reasonable default parameters */
+       io.generic.level = RAW_OPEN_NTCREATEX;
+       io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+       io.ntcreatex.in.root_fid = 0;
+       io.ntcreatex.in.alloc_size = 0;
+       io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+       io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_READONLY;
+       io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+       io.ntcreatex.in.create_options = 0;
+       io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+       io.ntcreatex.in.security_flags = 0;
+       io.ntcreatex.in.fname = fname;
+
+       /* Create the readonly file. */
+
+       status = smb_raw_open(cli->tree, tctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.file.fnum;
+
+       CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+       io.ntcreatex.in.create_options = 0;
+       CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED);
+       CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+       smbcli_close(cli->tree, fnum);
+
+       /* Now try and open for delete only - should succeed. */
+       io.ntcreatex.in.access_mask = SEC_STD_DELETE;
+       io.ntcreatex.in.file_attr = 0;
+       io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+       status = smb_raw_open(cli->tree, tctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       smbcli_unlink(cli->tree, fname);
+
+done:
+       smbcli_close(cli->tree, fnum);
+       smbcli_unlink(cli->tree, fname);
+
+       return ret;
+}
+
+
 /* basic testing of all RAW_OPEN_* calls 
 */
 bool torture_raw_open(struct torture_context *torture, struct smbcli_state *cli)
@@ -1480,6 +1670,8 @@ bool torture_raw_open(struct torture_context *torture, struct smbcli_state *cli)
        ret &= test_ctemp(cli, torture);
        ret &= test_chained(cli, torture);
        ret &= test_no_leading_slash(cli, torture);
+       ret &= test_openx_over_dir(cli, torture);
+       ret &= test_open_for_delete(cli, torture);
 
        smb_raw_exit(cli->session);
        smbcli_deltree(cli->tree, BASEDIR);