Merge branch 'master' of ssh://jra@git.samba.org/data/git/samba
[kai/samba-autobuild/.git] / source4 / torture / raw / unlink.c
index 9c408105dfd338fae7f9038c1a5c9d5084565a70..28218b26fddc0880b06040c6935f12884ed5a14b 100644 (file)
@@ -5,7 +5,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
+#include "torture/torture.h"
+#include "system/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
 
 #define CHECK_STATUS(status, correct) do { \
        if (!NT_STATUS_EQUAL(status, correct)) { \
                printf("(%s) Incorrect status %s - should be %s\n", \
                       __location__, nt_errstr(status), nt_errstr(correct)); \
-               ret = False; \
+               ret = false; \
                goto done; \
        }} while (0)
 
 /*
   test unlink ops
 */
-static BOOL test_unlink(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+static bool test_unlink(struct torture_context *tctx, struct smbcli_state *cli)
 {
-       struct smb_unlink io;
+       union smb_unlink io;
        NTSTATUS status;
-       BOOL ret = True;
+       bool ret = true;
        const char *fname = BASEDIR "\\test.txt";
 
-       if (smbcli_deltree(cli->tree, BASEDIR) == -1 ||
-           NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, BASEDIR))) {
-               printf("Unable to setup %s - %s\n", BASEDIR, smbcli_errstr(cli->tree));
-               return False;
+       if (!torture_setup_dir(cli, BASEDIR)) {
+               return false;
        }
 
        printf("Trying non-existant file\n");
-       io.in.pattern = fname;
-       io.in.attrib = 0;
+       io.unlink.in.pattern = fname;
+       io.unlink.in.attrib = 0;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 
        smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE));
 
-       io.in.pattern = fname;
-       io.in.attrib = 0;
+       io.unlink.in.pattern = fname;
+       io.unlink.in.attrib = 0;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_OK);
 
@@ -63,91 +66,125 @@ static BOOL test_unlink(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE));
        torture_set_file_attribute(cli->tree, fname, FILE_ATTRIBUTE_HIDDEN);
 
-       io.in.pattern = fname;
-       io.in.attrib = 0;
+       io.unlink.in.pattern = fname;
+       io.unlink.in.attrib = 0;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
 
-       io.in.pattern = fname;
-       io.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+       io.unlink.in.pattern = fname;
+       io.unlink.in.attrib = FILE_ATTRIBUTE_HIDDEN;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_OK);
 
+       io.unlink.in.pattern = fname;
+       io.unlink.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
        printf("Trying a directory\n");
-       io.in.pattern = BASEDIR;
-       io.in.attrib = 0;
+       io.unlink.in.pattern = BASEDIR;
+       io.unlink.in.attrib = 0;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
 
-       io.in.pattern = BASEDIR;
-       io.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+       io.unlink.in.pattern = BASEDIR;
+       io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
 
        printf("Trying a bad path\n");
-       io.in.pattern = "..";
-       io.in.attrib = 0;
+       io.unlink.in.pattern = "..";
+       io.unlink.in.attrib = 0;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
 
-       io.in.pattern = "\\..";
-       io.in.attrib = 0;
+       io.unlink.in.pattern = "\\..";
+       io.unlink.in.attrib = 0;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
 
-       io.in.pattern = BASEDIR "\\..\\..";
-       io.in.attrib = 0;
+       io.unlink.in.pattern = BASEDIR "\\..\\..";
+       io.unlink.in.attrib = 0;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
 
-       io.in.pattern = BASEDIR "\\..";
-       io.in.attrib = 0;
+       io.unlink.in.pattern = BASEDIR "\\..";
+       io.unlink.in.attrib = 0;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
 
        printf("Trying wildcards\n");
        smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE));
-       io.in.pattern = BASEDIR "\\t*.t";
-       io.in.attrib = 0;
+       io.unlink.in.pattern = BASEDIR "\\t*.t";
+       io.unlink.in.attrib = 0;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
 
-       io.in.pattern = BASEDIR "\\z*";
-       io.in.attrib = 0;
+       io.unlink.in.pattern = BASEDIR "\\z*";
+       io.unlink.in.attrib = 0;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
 
-       io.in.pattern = BASEDIR "\\z*";
-       io.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+       io.unlink.in.pattern = BASEDIR "\\z*";
+       io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
        status = smb_raw_unlink(cli->tree, &io);
-       CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
 
-       io.in.pattern = BASEDIR "\\*";
-       io.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+       if (torture_setting_bool(tctx, "samba3", false)) {
+               /*
+                * In Samba3 we gave up upon getting the error codes in
+                * wildcard unlink correct. Trying gentest showed that this is
+                * irregular beyond our capabilities. So for
+                * FILE_ATTRIBUTE_DIRECTORY we always return NAME_INVALID.
+                * Tried by jra and vl. If others feel like solving this
+                * puzzle, please tell us :-)
+                */
+               CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+       }
+       else {
+               CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+       }
+
+       io.unlink.in.pattern = BASEDIR "\\*";
+       io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
 
-       io.in.pattern = BASEDIR "\\?";
-       io.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+       io.unlink.in.pattern = BASEDIR "\\?";
+       io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
 
-       io.in.pattern = BASEDIR "\\t*";
-       io.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+       io.unlink.in.pattern = BASEDIR "\\t*";
+       io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
        status = smb_raw_unlink(cli->tree, &io);
-       CHECK_STATUS(status, NT_STATUS_OK);
+       if (torture_setting_bool(tctx, "samba3", false)) {
+               CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+       }
+       else {
+               CHECK_STATUS(status, NT_STATUS_OK);
+       }
 
        smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE));
 
-       io.in.pattern = BASEDIR "\\*.dat";
-       io.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+       io.unlink.in.pattern = BASEDIR "\\*.dat";
+       io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
        status = smb_raw_unlink(cli->tree, &io);
-       CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+       if (torture_setting_bool(tctx, "samba3", false)) {
+               CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+       }
+       else {
+               CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+       }
 
-       io.in.pattern = BASEDIR "\\*.tx?";
-       io.in.attrib = 0;
+       io.unlink.in.pattern = BASEDIR "\\*.tx?";
+       io.unlink.in.attrib = 0;
        status = smb_raw_unlink(cli->tree, &io);
-       CHECK_STATUS(status, NT_STATUS_OK);
+       if (torture_setting_bool(tctx, "samba3", false)) {
+               CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+       }
+       else {
+               CHECK_STATUS(status, NT_STATUS_OK);
+       }
 
        status = smb_raw_unlink(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
@@ -160,26 +197,353 @@ done:
 }
 
 
-/* 
-   basic testing of unlink calls
+/*
+  test delete on close 
 */
-BOOL torture_raw_unlink(int dummy)
+static bool test_delete_on_close(struct torture_context *tctx, 
+                                                                struct smbcli_state *cli)
 {
-       struct smbcli_state *cli;
-       BOOL ret = True;
-       TALLOC_CTX *mem_ctx;
+       union smb_open op;
+       union smb_unlink io;
+       struct smb_rmdir dio;
+       NTSTATUS status;
+       bool ret = true;
+       int fnum, fnum2;
+       const char *fname = BASEDIR "\\test.txt";
+       const char *dname = BASEDIR "\\test.dir";
+       const char *inside = BASEDIR "\\test.dir\\test.txt";
+       union smb_setfileinfo sfinfo;
 
-       if (!torture_open_connection(&cli)) {
-               return False;
+       if (!torture_setup_dir(cli, BASEDIR)) {
+               return false;
        }
 
-       mem_ctx = talloc_init("torture_raw_unlink");
+       dio.in.path = dname;
+
+       io.unlink.in.pattern = fname;
+       io.unlink.in.attrib = 0;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+       printf("Testing with delete_on_close 0\n");
+       fnum = create_complex_file(cli, tctx, fname);
+
+       sfinfo.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO;
+       sfinfo.disposition_info.in.file.fnum = fnum;
+       sfinfo.disposition_info.in.delete_on_close = 0;
+       status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       smbcli_close(cli->tree, fnum);
+
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("Testing with delete_on_close 1\n");
+       fnum = create_complex_file(cli, tctx, fname);
+       sfinfo.disposition_info.in.file.fnum = fnum;
+       sfinfo.disposition_info.in.delete_on_close = 1;
+       status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       smbcli_close(cli->tree, fnum);
+
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+
+       printf("Testing with directory and delete_on_close 0\n");
+       status = create_directory_handle(cli->tree, dname, &fnum);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       sfinfo.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO;
+       sfinfo.disposition_info.in.file.fnum = fnum;
+       sfinfo.disposition_info.in.delete_on_close = 0;
+       status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       smbcli_close(cli->tree, fnum);
+
+       status = smb_raw_rmdir(cli->tree, &dio);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("Testing with directory delete_on_close 1\n");
+       status = create_directory_handle(cli->tree, dname, &fnum);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       
+       sfinfo.disposition_info.in.file.fnum = fnum;
+       sfinfo.disposition_info.in.delete_on_close = 1;
+       status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       smbcli_close(cli->tree, fnum);
+
+       status = smb_raw_rmdir(cli->tree, &dio);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+
+       if (!torture_setting_bool(tctx, "samba3", false)) {
+
+               /*
+                * Known deficiency, also skipped in base-delete.
+                */
+
+               printf("Testing with non-empty directory delete_on_close\n");
+               status = create_directory_handle(cli->tree, dname, &fnum);
+               CHECK_STATUS(status, NT_STATUS_OK);
+
+               fnum2 = create_complex_file(cli, tctx, inside);
+
+               sfinfo.disposition_info.in.file.fnum = fnum;
+               sfinfo.disposition_info.in.delete_on_close = 1;
+               status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+               CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
+
+               sfinfo.disposition_info.in.file.fnum = fnum2;
+               status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+               CHECK_STATUS(status, NT_STATUS_OK);
+
+               sfinfo.disposition_info.in.file.fnum = fnum;
+               status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+               CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
 
-       if (!test_unlink(cli, mem_ctx)) {
-               ret = False;
+               smbcli_close(cli->tree, fnum2);
+
+               status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+               CHECK_STATUS(status, NT_STATUS_OK);
+
+               smbcli_close(cli->tree, fnum);
+
+               status = smb_raw_rmdir(cli->tree, &dio);
+               CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+       }
+
+       printf("Testing open dir with delete_on_close\n");
+       status = create_directory_handle(cli->tree, dname, &fnum);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       
+       smbcli_close(cli->tree, fnum);
+       fnum2 = create_complex_file(cli, tctx, inside);
+       smbcli_close(cli->tree, fnum2);
+
+       op.generic.level = RAW_OPEN_NTCREATEX;
+       op.ntcreatex.in.root_fid.fnum = 0;
+       op.ntcreatex.in.flags = 0;
+       op.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+       op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY |NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+       op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+       op.ntcreatex.in.alloc_size = 0;
+       op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+       op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+       op.ntcreatex.in.security_flags = 0;
+       op.ntcreatex.in.fname = dname;
+
+       status = smb_raw_open(cli->tree, tctx, &op);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = op.ntcreatex.out.file.fnum;
+
+       smbcli_close(cli->tree, fnum);
+
+       status = smb_raw_rmdir(cli->tree, &dio);
+       CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
+
+       smbcli_deltree(cli->tree, dname);
+
+       printf("Testing double open dir with second delete_on_close\n");
+       status = create_directory_handle(cli->tree, dname, &fnum);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       smbcli_close(cli->tree, fnum);
+       
+       fnum2 = create_complex_file(cli, tctx, inside);
+       smbcli_close(cli->tree, fnum2);
+
+       op.generic.level = RAW_OPEN_NTCREATEX;
+       op.ntcreatex.in.root_fid.fnum = 0;
+       op.ntcreatex.in.flags = 0;
+       op.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+       op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY |NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+       op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+       op.ntcreatex.in.alloc_size = 0;
+       op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+       op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+       op.ntcreatex.in.security_flags = 0;
+       op.ntcreatex.in.fname = dname;
+
+       status = smb_raw_open(cli->tree, tctx, &op);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum2 = op.ntcreatex.out.file.fnum;
+
+       smbcli_close(cli->tree, fnum2);
+
+       status = smb_raw_rmdir(cli->tree, &dio);
+       CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
+
+       smbcli_deltree(cli->tree, dname);
+
+       printf("Testing pre-existing open dir with second delete_on_close\n");
+       status = create_directory_handle(cli->tree, dname, &fnum);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       
+       smbcli_close(cli->tree, fnum);
+
+       fnum = create_complex_file(cli, tctx, inside);
+       smbcli_close(cli->tree, fnum);
+
+       /* we have a dir with a file in it, no handles open */
+
+       op.generic.level = RAW_OPEN_NTCREATEX;
+       op.ntcreatex.in.root_fid.fnum = 0;
+       op.ntcreatex.in.flags = 0;
+       op.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+       op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY |NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+       op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
+       op.ntcreatex.in.alloc_size = 0;
+       op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+       op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+       op.ntcreatex.in.security_flags = 0;
+       op.ntcreatex.in.fname = dname;
+
+       status = smb_raw_open(cli->tree, tctx, &op);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = op.ntcreatex.out.file.fnum;
+
+       /* open without delete on close */
+       op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+       status = smb_raw_open(cli->tree, tctx, &op);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum2 = op.ntcreatex.out.file.fnum;
+
+       /* close 2nd file handle */
+       smbcli_close(cli->tree, fnum2);
+
+       status = smb_raw_rmdir(cli->tree, &dio);
+       CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
+       
+
+       smbcli_close(cli->tree, fnum);
+
+       status = smb_raw_rmdir(cli->tree, &dio);
+       CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
+       
+done:
+       smb_raw_exit(cli->session);
+       smbcli_deltree(cli->tree, BASEDIR);
+       return ret;
+}
+
+
+struct unlink_defer_cli_state {
+       struct torture_context *tctx;
+       struct smbcli_state *cli1;
+};
+
+/*
+ * A handler function for oplock break requests. Ack it as a break to none
+ */
+static bool oplock_handler_ack_to_none(struct smbcli_transport *transport,
+                                      uint16_t tid, uint16_t fnum,
+                                      uint8_t level, void *private_data)
+{
+       struct unlink_defer_cli_state *ud_cli_state =
+           (struct unlink_defer_cli_state *)private_data;
+       union smb_setfileinfo sfinfo;
+       NTSTATUS status;
+       bool ret;
+       struct smbcli_request *req = NULL;
+
+       torture_comment(ud_cli_state->tctx, "delete the file before sending "
+                       "the ack.");
+
+       /* cli1: set delete on close */
+       sfinfo.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO;
+       sfinfo.disposition_info.in.file.fnum = fnum;
+       sfinfo.disposition_info.in.delete_on_close = 1;
+       req = smb_raw_setfileinfo_send(ud_cli_state->cli1->tree, &sfinfo);
+
+       smbcli_close(ud_cli_state->cli1->tree, fnum);
+
+       torture_comment(ud_cli_state->tctx, "Acking the oplock to NONE\n");
+
+       ret = smbcli_oplock_ack(ud_cli_state->cli1->tree, fnum,
+                                OPLOCK_BREAK_TO_NONE);
+ done:
+       return ret;
+}
+
+static bool test_unlink_defer(struct torture_context *tctx,
+                             struct smbcli_state *cli1,
+                             struct smbcli_state *cli2)
+{
+       const char *fname = BASEDIR "\\test_unlink_defer.dat";
+       NTSTATUS status;
+       bool ret = true;
+       union smb_open io;
+       union smb_unlink unl;
+       uint16_t fnum=0;
+       struct unlink_defer_cli_state ud_cli_state = {};
+
+       if (!torture_setup_dir(cli1, BASEDIR)) {
+               return false;
        }
 
-       torture_close_connection(cli);
-       talloc_destroy(mem_ctx);
+       /* cleanup */
+       smbcli_unlink(cli1->tree, fname);
+
+       ud_cli_state.tctx = tctx;
+       ud_cli_state.cli1 = cli1;
+
+       smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_none,
+                             &ud_cli_state);
+
+       io.generic.level = RAW_OPEN_NTCREATEX;
+       io.ntcreatex.in.root_fid = 0;
+       io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+       io.ntcreatex.in.alloc_size = 0;
+       io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       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_IF;
+       io.ntcreatex.in.create_options = 0;
+       io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+       io.ntcreatex.in.security_flags = 0;
+       io.ntcreatex.in.fname = fname;
+
+       /* cli1: open file with a batch oplock. */
+       io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+           NTCREATEX_FLAGS_REQUEST_OPLOCK |
+           NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+
+       status = smb_raw_open(cli1->tree, tctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.file.fnum;
+
+       /* cli2: Try to unlink it, but block on the oplock */
+       torture_comment(tctx, "Try an unlink (should defer the open\n");
+       unl.unlink.in.pattern = fname;
+       unl.unlink.in.attrib = 0;
+       status = smb_raw_unlink(cli2->tree, &unl);
+
+done:
+       smb_raw_exit(cli1->session);
+       smb_raw_exit(cli2->session);
+       smbcli_deltree(cli1->tree, BASEDIR);
        return ret;
 }
+
+/* 
+   basic testing of unlink calls
+*/
+struct torture_suite *torture_raw_unlink(TALLOC_CTX *mem_ctx)
+{
+       struct torture_suite *suite = torture_suite_create(mem_ctx, "UNLINK");
+
+       torture_suite_add_1smb_test(suite, "unlink", test_unlink);
+       torture_suite_add_1smb_test(suite, "delete_on_close", test_delete_on_close);
+       torture_suite_add_2smb_test(suite, "UNLINK-DEFER", test_unlink_defer);
+
+       return suite;
+}