s3-torture: Use static printer for smbd spooler test
[ddiss/samba.git] / source4 / torture / rpc / spoolss.c
index 114b3e1493ed826987d2df597e152975ecae668e..6e0d9edcf888273b5114115e1fbeb3937c4bc981 100644 (file)
@@ -5,7 +5,7 @@
    Copyright (C) Tim Potter 2003
    Copyright (C) Stefan Metzmacher 2005
    Copyright (C) Jelmer Vernooij 2007
-   Copyright (C) Guenther Deschner 2009-2010
+   Copyright (C) Guenther Deschner 2009-2011
 
    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
 #include "libcli/libcli.h"
 #include "libcli/raw/raw_proto.h"
 #include "libcli/resolve/resolve.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
 #include "lib/cmdline/popt_common.h"
 #include "system/filesys.h"
 #include "torture/ndr/ndr.h"
+#include "torture/smb2/proto.h"
 
 #define TORTURE_WELLKNOWN_PRINTER      "torture_wkn_printer"
 #define TORTURE_PRINTER                        "torture_printer"
 #define TORTURE_DRIVER_ADOBE           "torture_driver_adobe"
 #define TORTURE_DRIVER_EX_ADOBE                "torture_driver_ex_adobe"
 #define TORTURE_DRIVER_ADOBE_CUPSADDSMB        "torture_driver_adobe_cupsaddsmb"
+#define TORTURE_DRIVER_TIMESTAMPS      "torture_driver_timestamps"
+#define TORTURE_DRIVER_DELETER         "torture_driver_deleter"
+#define TORTURE_DRIVER_DELETERIN       "torture_driver_deleterin"
+#define TORTURE_PRINTER_STATIC1                "print1"
 
 #define TOP_LEVEL_PRINT_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print"
 #define TOP_LEVEL_PRINT_PRINTERS_KEY TOP_LEVEL_PRINT_KEY "\\Printers"
@@ -2134,7 +2141,7 @@ static bool test_devicemode_full(struct torture_context *tctx,
        bool ret = true;
        NTSTATUS status;
 
-#define TEST_DEVMODE_INT_EXP(lvl1, field1, lvl2, field2, value, exp_value) do { \
+#define TEST_DEVMODE_INT_EXP_RESULT(lvl1, field1, lvl2, field2, value, exp_value, expected_result) do { \
                torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
                q.in.level = lvl1; \
                TESTGETCALL(GetPrinter, q) \
@@ -2146,16 +2153,22 @@ static bool test_devicemode_full(struct torture_context *tctx,
                }\
                devmode_ctr.devmode = q.out.info->info ## lvl1.devmode; \
                devmode_ctr.devmode->field1 = value; \
-               TESTSETCALL(SetPrinter, s) \
-               TESTGETCALL(GetPrinter, q) \
-               INT_EQUAL(q.out.info->info ## lvl1.devmode->field1, exp_value, field1); \
-               q.in.level = lvl2; \
-               TESTGETCALL(GetPrinter, q) \
-               INT_EQUAL(q.out.info->info ## lvl2.devmode->field2, exp_value, field1); \
+               TESTSETCALL_EXP(SetPrinter, s, expected_result) \
+               if (W_ERROR_IS_OK(expected_result)) { \
+                       TESTGETCALL(GetPrinter, q) \
+                       INT_EQUAL(q.out.info->info ## lvl1.devmode->field1, exp_value, field1); \
+                       q.in.level = lvl2; \
+                       TESTGETCALL(GetPrinter, q) \
+                       INT_EQUAL(q.out.info->info ## lvl2.devmode->field2, exp_value, field1); \
+               }\
        } while (0)
 
+#define TEST_DEVMODE_INT_EXP(lvl1, field1, lvl2, field2, value, expected_result) do { \
+        TEST_DEVMODE_INT_EXP_RESULT(lvl1, field1, lvl2, field2, value, value, expected_result); \
+        } while (0)
+
 #define TEST_DEVMODE_INT(lvl1, field1, lvl2, field2, value) do { \
-        TEST_DEVMODE_INT_EXP(lvl1, field1, lvl2, field2, value, value); \
+        TEST_DEVMODE_INT_EXP_RESULT(lvl1, field1, lvl2, field2, value, value, WERR_OK); \
         } while (0)
 
        ZERO_STRUCT(devmode_ctr);
@@ -2176,10 +2189,22 @@ static bool test_devicemode_full(struct torture_context *tctx,
        const char *devicename;/* [charset(UTF16)] */
        enum spoolss_DeviceModeSpecVersion specversion;
        uint16_t driverversion;
-       uint16_t size;
        uint16_t __driverextra_length;/* [value(r->driverextra_data.length)] */
        uint32_t fields;
 #endif
+       TEST_DEVMODE_INT_EXP(8, size,           8, size, __LINE__, WERR_INVALID_PARAM);
+       TEST_DEVMODE_INT_EXP(8, size,           8, size, 0, WERR_INVALID_PARAM);
+       TEST_DEVMODE_INT_EXP(8, size,           8, size, 0xffff, WERR_INVALID_PARAM);
+       TEST_DEVMODE_INT_EXP(8, size,           8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0), (devmode_ctr.devmode->__driverextra_length > 0 ) ? WERR_INVALID_PARAM : WERR_OK);
+       TEST_DEVMODE_INT(8, size,               8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0) - devmode_ctr.devmode->__driverextra_length);
+
+       devmode_ctr.devmode->driverextra_data = data_blob_string_const("foobar");
+       torture_assert(tctx,
+               test_devmode_set_level(tctx, b, handle, 8, devmode_ctr.devmode),
+               "failed to set devmode");
+
+       TEST_DEVMODE_INT_EXP(8, size,           8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0), (devmode_ctr.devmode->__driverextra_length > 0 ) ? WERR_INVALID_PARAM : WERR_OK);
+       TEST_DEVMODE_INT(8, size,               8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0) - devmode_ctr.devmode->__driverextra_length);
 
        TEST_DEVMODE_INT(8, orientation,        8, orientation, __LINE__);
        TEST_DEVMODE_INT(8, papersize,          8, papersize, __LINE__);
@@ -2238,10 +2263,20 @@ static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
 
        devmode = info.info8.devmode;
 
+       if (devmode && devmode->size == 0) {
+               torture_fail(tctx,
+                       "devmode of zero size!");
+       }
+
        torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
 
        devmode2 = info.info2.devmode;
 
+       if (devmode2 && devmode2->size == 0) {
+               torture_fail(tctx,
+                       "devmode of zero size!");
+       }
+
        torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2),
                "DM level 8 != DM level 2");
 
@@ -2383,6 +2418,11 @@ static bool test_PrinterInfo_DevMode(struct torture_context *tctx,
 
        devmode = info.info8.devmode;
 
+       if (devmode && devmode->size == 0) {
+               torture_fail(tctx,
+                       "devmode of zero size!");
+       }
+
        if (addprinter_devmode) {
                if (!test_devicemode_equal(tctx, devmode, addprinter_devmode)) {
                        torture_warning(tctx, "current global DM is != DM provided in addprinter");
@@ -3222,6 +3262,7 @@ static bool test_EnumJobs_args(struct torture_context *tctx,
 static bool test_DoPrintTest_add_one_job(struct torture_context *tctx,
                                         struct dcerpc_binding_handle *b,
                                         struct policy_handle *handle,
+                                        const char *document_name,
                                         uint32_t *job_id)
 {
        NTSTATUS status;
@@ -3240,7 +3281,7 @@ static bool test_DoPrintTest_add_one_job(struct torture_context *tctx,
        s.in.level              = 1;
        s.in.info.info1         = &info1;
        s.out.job_id            = job_id;
-       info1.document_name     = "TorturePrintJob";
+       info1.document_name     = document_name;
        info1.output_file       = NULL;
        info1.datatype          = "RAW";
 
@@ -3383,7 +3424,7 @@ static bool test_DoPrintTest(struct torture_context *tctx,
        job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
 
        for (i=0; i < num_jobs; i++) {
-               ret &= test_DoPrintTest_add_one_job(tctx, b, handle, &job_ids[i]);
+               ret &= test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]);
        }
 
        for (i=0; i < num_jobs; i++) {
@@ -3410,7 +3451,7 @@ static bool test_DoPrintTest_extended(struct torture_context *tctx,
        job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
 
        for (i=0; i < num_jobs; i++) {
-               ret &= test_DoPrintTest_add_one_job(tctx, b, handle, &job_ids[i]);
+               ret &= test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]);
        }
 
        ret &= test_DoPrintTest_check_jobs(tctx, b, handle, num_jobs, job_ids);
@@ -4369,6 +4410,23 @@ do {\
                talloc_asprintf(tctx, "%s - %s mismatch", #wname, #iname));\
 } while(0);
 
+#define test_binary(wname, iname) \
+do {\
+       enum winreg_Type w_type;\
+       uint32_t w_size;\
+       uint32_t w_length;\
+       uint8_t *w_data;\
+       torture_assert(tctx,\
+               test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+                                      &w_type, &w_size, &w_length, &w_data),\
+               "failed to query winreg");\
+       torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\
+       torture_assert_int_equal(tctx, w_size, iname.length, "unexpected length");\
+       torture_assert_mem_equal(tctx, w_data, iname.data, w_size, \
+               "binary unequal");\
+} while(0);
+
+
 #define test_dm(wname, iname) \
 do {\
        DATA_BLOB blob;\
@@ -4575,6 +4633,11 @@ static const char *driver_winreg_date(TALLOC_CTX *mem_ctx, NTTIME nt)
 {
        time_t t;
        struct tm *tm;
+
+       if (nt == 0) {
+               return talloc_strdup(mem_ctx, "01/01/1601");
+       }
+
        t = nt_time_to_unix(nt);
        tm = localtime(&t);
 
@@ -4631,11 +4694,8 @@ static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
                test_winreg_OpenKey(tctx, winreg_handle, hive_handle, driver_key, &key_handle),
                "failed to open driver key");
 
-       if (torture_setting_bool(tctx, "samba3", false)) {
-               goto try_level3;
-       }
-
-       if (torture_setting_bool(tctx, "w2k3", false)) {
+       if (torture_setting_bool(tctx, "samba3", false) ||
+           torture_setting_bool(tctx, "w2k3", false)) {
                goto try_level6;
        }
 
@@ -4717,8 +4777,16 @@ static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
        test_sz("Data File",                    data_file);
        test_sz("Datatype",                     info.info6.default_datatype);
        test_sz("Driver",                       driver_path);
-       test_sz("DriverDate",                   driver_date);
-       test_sz("DriverVersion",                driver_version);
+       if (torture_setting_bool(tctx, "w2k3", false)) {
+               DATA_BLOB blob = data_blob_talloc_zero(tctx, 8);
+               push_nttime(blob.data, 0, info.info6.driver_date);
+               test_binary("DriverDate",       blob);
+               SBVAL(blob.data, 0, info.info6.driver_version);
+               test_binary("DriverVersion",    blob);
+       } else {
+               test_sz("DriverDate",           driver_date);
+               test_sz("DriverVersion",        driver_version);
+       }
        test_sz("HardwareID",                   info.info6.hardware_id);
        test_sz("Help File",                    help_file);
        test_sz("Manufacturer",                 info.info6.manufacturer_name);
@@ -4731,8 +4799,6 @@ static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
        test_dword("Version",                   info.info6.version);
 /*     test_dword("TempDir",                   ?); */
 
- try_level3:
-
        if (handle) {
                torture_assert(tctx,
                        test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 3, version, 0, &info, &result),
@@ -5437,6 +5503,7 @@ do {\
 
        TEST_SET_SZ("description", comment, "newval");
        TEST_SET_SZ("location", location, "newval");
+       TEST_SET_SZ("driverName", drivername, "newval");
 /*     TEST_SET_DWORD("priority", priority, 25); */
 
        torture_assert(tctx,
@@ -7525,8 +7592,9 @@ static bool torture_rpc_spoolss_printerwkn_setup(struct torture_context *tctx, v
        t->info2.printername    = TORTURE_WELLKNOWN_PRINTER;
        t->devmode              = NULL;
 
-       if (t->wellknown && torture_setting_bool(tctx, "samba3", false)) {
-               torture_skip(tctx, "skipping AddPrinter level 1 against samba");
+       /* FIXME */
+       if (t->wellknown) {
+               torture_skip(tctx, "skipping AddPrinter level 1");
        }
 
        return torture_rpc_spoolss_printer_setup_common(tctx, t);
@@ -7543,8 +7611,9 @@ static bool torture_rpc_spoolss_printerexwkn_setup(struct torture_context *tctx,
        t->info2.printername    = TORTURE_WELLKNOWN_PRINTER_EX;
        t->devmode              = NULL;
 
-       if (t->wellknown && torture_setting_bool(tctx, "samba3", false)) {
-               torture_skip(tctx, "skipping AddPrinter level 1 against samba");
+       /* FIXME */
+       if (t->wellknown) {
+               torture_skip(tctx, "skipping AddPrinterEx level 1");
        }
 
        return torture_rpc_spoolss_printer_setup_common(tctx, t);
@@ -7661,6 +7730,82 @@ static bool test_print_test_extended(struct torture_context *tctx,
        return ret;
 }
 
+/* use smbd file IO to spool a print job */
+static bool test_print_test_smbd(struct torture_context *tctx,
+                                void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       NTSTATUS status;
+       uint32_t count;
+       union spoolss_JobInfo *info = NULL;
+       int i;
+
+       struct smb2_tree *tree;
+       struct smb2_handle job_h;
+       struct cli_credentials *credentials = cmdline_credentials;
+       struct smbcli_options options;
+       TALLOC_CTX *mem_ctx = talloc_new(tctx);
+       /*
+        * Do not test against the dynamically added printers, printing via
+        * smbd means that a different spoolss process may handle the
+        * OpenPrinter request to the one that handled the AddPrinter request.
+        * This currently leads to an ugly race condition where one process
+        * sees the new printer and one doesn't.
+        */
+       const char *share = TORTURE_PRINTER_STATIC1;
+
+       torture_comment(tctx, "Testing smbd job spooling\n");
+       lpcfg_smbcli_options(tctx->lp_ctx, &options);
+
+       status = smb2_connect_ext(mem_ctx,
+                                 torture_setting_string(tctx, "host", NULL),
+                                 lpcfg_smb_ports(tctx->lp_ctx),
+                                 share,
+                                 lpcfg_resolve_context(tctx->lp_ctx),
+                                 credentials,
+                                 0,
+                                 &tree,
+                                 tctx->ev,
+                                 &options,
+                                 lpcfg_socket_options(tctx->lp_ctx),
+                                 lpcfg_gensec_settings(tctx, tctx->lp_ctx));
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("Failed to connect to SMB2 printer %s - %s\n",
+                      share, nt_errstr(status));
+               return false;
+       }
+
+       status = torture_smb2_testfile(tree, "smbd_spooler_job", &job_h);
+       torture_assert_ntstatus_ok(tctx, status, "smbd spool job create");
+
+       status = smb2_util_write(tree, job_h, "exciting print job data", 0,
+                                sizeof("exciting print job data"));
+       torture_assert_ntstatus_ok(tctx, status, "smbd spool job write");
+
+       /* check back end spoolss job was created */
+       torture_assert(tctx,
+               test_EnumJobs_args(tctx, b, &t->handle, 1, &count, &info),
+               "EnumJobs level 1 failed");
+
+       for (i = 0; i < count; i++) {
+               if (!strcmp(info[i].info1.document_name, "smbd_spooler_job")) {
+                       break;
+               }
+       }
+       torture_assert(tctx, (i != count), "smbd_spooler_job not found");
+
+       status = smb2_util_close(tree, job_h);
+       torture_assert_ntstatus_ok(tctx, status, "smbd spool job close");
+
+       /* disconnect from printer share */
+       talloc_free(mem_ctx);
+
+       return true;
+}
+
 static bool test_printer_sd(struct torture_context *tctx,
                            void *private_data)
 {
@@ -7842,6 +7987,7 @@ void torture_tcase_printer(struct torture_tcase *tcase)
        torture_tcase_add_simple_test(tcase, "csetprinter", test_csetprinter);
        torture_tcase_add_simple_test(tcase, "print_test", test_print_test);
        torture_tcase_add_simple_test(tcase, "print_test_extended", test_print_test_extended);
+       torture_tcase_add_simple_test(tcase, "print_test_smbd", test_print_test_smbd);
        torture_tcase_add_simple_test(tcase, "printer_info", test_printer_info);
        torture_tcase_add_simple_test(tcase, "sd", test_printer_sd);
        torture_tcase_add_simple_test(tcase, "dm", test_printer_dm);
@@ -8421,6 +8567,9 @@ static bool test_AddPrinterDriver_args_level_6(struct torture_context *tctx,
                }
        }
 
+       torture_assert_nttime_equal(tctx, info.info6.driver_date, info6.driver_date, "driverdate mismatch");
+       torture_assert_u64_equal(tctx, info.info6.driver_version, info6.driver_version, "driverversion mismatch");
+
        return true;
 }
 
@@ -8469,6 +8618,9 @@ static bool test_AddPrinterDriver_args_level_8(struct torture_context *tctx,
                }
        }
 
+       torture_assert_nttime_equal(tctx, info.info8.driver_date, r->driver_date, "driverdate mismatch");
+       torture_assert_u64_equal(tctx, info.info8.driver_version, r->driver_version, "driverversion mismatch");
+
        return true;
 }
 
@@ -8628,6 +8780,25 @@ static bool test_PrinterDriver_args(struct torture_context *tctx,
                return ret;
        }
 
+       {
+               struct dcerpc_pipe *p2;
+               struct policy_handle hive_handle;
+               struct dcerpc_binding_handle *b2;
+
+               torture_assert_ntstatus_ok(tctx,
+                       torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+                       "could not open winreg pipe");
+               b2 = p2->binding_handle;
+
+               torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+               ret = test_GetDriverInfo_winreg(tctx, b, NULL, NULL, r->driver_name, r->architecture, r->version, b2, &hive_handle, server_name);
+
+               test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+               talloc_free(p2);
+       }
+
        if (ex) {
                return test_DeletePrinterDriverEx(tctx, b, server_name, r->driver_name, r->architecture, delete_flags, r->version);
        } else {
@@ -8835,6 +9006,84 @@ static bool upload_printer_driver(struct torture_context *tctx,
        return true;
 }
 
+static bool check_printer_driver_file(struct torture_context *tctx,
+                                     struct smbcli_state *cli,
+                                     struct torture_driver_context *d,
+                                     const char *file_name)
+{
+       const char *remote_arch_dir = driver_directory_dir(d->remote.driver_directory);
+       const char *remote_name = talloc_asprintf(tctx, "%s\\%d\\%s",
+                                                 remote_arch_dir,
+                                                 d->info8.version,
+                                                 file_name);
+       int fnum;
+
+       torture_assert(tctx, (file_name && strlen(file_name) != 0), "invalid filename");
+
+       torture_comment(tctx, "checking for driver file at %s\n", remote_name);
+
+       fnum = smbcli_open(cli->tree, remote_name, O_RDONLY, DENY_NONE);
+       if (fnum == -1) {
+               return false;
+       }
+
+       torture_assert_ntstatus_ok(tctx,
+               smbcli_close(cli->tree, fnum),
+               "failed to close driver file");
+
+       return true;
+}
+
+static bool check_printer_driver_files(struct torture_context *tctx,
+                                      const char *server_name,
+                                      struct torture_driver_context *d,
+                                      bool expect_exist)
+{
+       struct smbcli_state *cli;
+       const char *share_name = driver_directory_share(tctx, d->remote.driver_directory);
+       int i;
+
+       torture_assert(tctx,
+               connect_printer_driver_share(tctx, server_name, share_name, &cli),
+               "failed to connect to driver share");
+
+       torture_comment(tctx, "checking %sexistent driver files at \\\\%s\\%s\n",
+                       (expect_exist ? "": "non-"),
+                       server_name, share_name);
+
+       if (d->info8.driver_path && d->info8.driver_path[0]) {
+               torture_assert(tctx,
+                       check_printer_driver_file(tctx, cli, d, d->info8.driver_path) == expect_exist,
+                       "failed driver_path check");
+       }
+       if (d->info8.data_file && d->info8.data_file[0]) {
+               torture_assert(tctx,
+                       check_printer_driver_file(tctx, cli, d, d->info8.data_file) == expect_exist,
+                       "failed data_file check");
+       }
+       if (d->info8.config_file && d->info8.config_file[0]) {
+               torture_assert(tctx,
+                       check_printer_driver_file(tctx, cli, d, d->info8.config_file) == expect_exist,
+                       "failed config_file check");
+       }
+       if (d->info8.help_file && d->info8.help_file[0]) {
+               torture_assert(tctx,
+                       check_printer_driver_file(tctx, cli, d, d->info8.help_file) == expect_exist,
+                       "failed help_file check");
+       }
+       if (d->info8.dependent_files) {
+               for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) {
+                       torture_assert(tctx,
+                               check_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]) == expect_exist,
+                               "failed dependent_files check");
+               }
+       }
+
+       talloc_free(cli);
+
+       return true;
+}
+
 static bool remove_printer_driver_file(struct torture_context *tctx,
                                       struct smbcli_state *cli,
                                       struct torture_driver_context *d,
@@ -8937,12 +9186,19 @@ static bool test_add_driver_arg(struct torture_context *tctx,
                upload_printer_driver(tctx, dcerpc_server_name(p), d),
                "failed to upload printer driver");
 
-       info8.version           = d->info8.version;
-       info8.driver_name       = d->info8.driver_name;
-       info8.architecture      = d->local.environment;
-       info8.driver_path       = d->info8.driver_path;
-       info8.data_file         = d->info8.data_file;
-       info8.config_file       = d->info8.config_file;
+       info8 = d->info8;
+       if (d->info8.dependent_files) {
+               info8.dependent_files = talloc_zero(tctx, struct spoolss_StringArray);
+               if (d->info8.dependent_files->string) {
+                       for (i=0; d->info8.dependent_files->string[i] != NULL; i++) {
+                       }
+                       info8.dependent_files->string = talloc_zero_array(info8.dependent_files, const char *, i+1);
+                       for (i=0; d->info8.dependent_files->string[i] != NULL; i++) {
+                               info8.dependent_files->string[i] = talloc_strdup(info8.dependent_files->string, d->info8.dependent_files->string[i]);
+                       }
+               }
+       }
+       info8.architecture      = d->local.environment;
 
        for (i=0; i < ARRAY_SIZE(levels); i++) {
 
@@ -8979,6 +9235,14 @@ static bool test_add_driver_arg(struct torture_context *tctx,
        if (d->info8.config_file) {
                info8.config_file       = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.config_file);
        }
+       if (d->info8.help_file) {
+               info8.help_file = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.help_file);
+       }
+       if (d->info8.dependent_files && d->info8.dependent_files->string) {
+               for (i=0; d->info8.dependent_files->string[i] != NULL; i++) {
+                       info8.dependent_files->string[i] = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.dependent_files->string[i]);
+               }
+       }
 
        for (i=0; i < ARRAY_SIZE(levels); i++) {
 
@@ -9104,6 +9368,10 @@ static bool test_add_driver_adobe(struct torture_context *tctx,
 {
        struct torture_driver_context *d;
 
+       if (!torture_setting_bool(tctx, "samba3", false)) {
+               torture_skip(tctx, "skipping adobe test which only works against samba3");
+       }
+
        d = talloc_zero(tctx, struct torture_driver_context);
 
        d->info8.version                = SPOOLSS_DRIVER_VERSION_9X;
@@ -9162,6 +9430,285 @@ static bool test_add_driver_adobe_cupsaddsmb(struct torture_context *tctx,
        return test_add_driver_arg(tctx, p, d);
 }
 
+static bool test_add_driver_timestamps(struct torture_context *tctx,
+                                      struct dcerpc_pipe *p)
+{
+       struct torture_driver_context *d;
+       struct timeval t = timeval_current();
+
+       d = talloc_zero(tctx, struct torture_driver_context);
+
+       d->info8.version                = SPOOLSS_DRIVER_VERSION_200X;
+       d->info8.driver_name            = TORTURE_DRIVER_TIMESTAMPS;
+       d->info8.architecture           = NULL;
+       d->info8.driver_path            = talloc_strdup(d, "pscript5.dll");
+       d->info8.data_file              = talloc_strdup(d, "cups6.ppd");
+       d->info8.config_file            = talloc_strdup(d, "cupsui6.dll");
+       d->info8.driver_date            = timeval_to_nttime(&t);
+       d->local.environment            = talloc_strdup(d, "Windows NT x86");
+       d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+       d->ex                           = true;
+
+       torture_assert(tctx,
+               test_add_driver_arg(tctx, p, d),
+               "");
+
+       unix_to_nt_time(&d->info8.driver_date, 1);
+
+       torture_assert(tctx,
+               test_add_driver_arg(tctx, p, d),
+               "");
+
+       return true;
+}
+
+static bool test_multiple_drivers(struct torture_context *tctx,
+                                 struct dcerpc_pipe *p)
+{
+       struct torture_driver_context *d;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+       int i;
+       struct spoolss_AddDriverInfo8 info8;
+       uint32_t add_flags = APD_COPY_NEW_FILES;
+       uint32_t delete_flags = 0;
+
+       d = talloc_zero(tctx, struct torture_driver_context);
+
+       d->info8.version                = SPOOLSS_DRIVER_VERSION_200X;
+       d->info8.driver_path            = talloc_strdup(d, "pscript5.dll");
+       d->info8.data_file              = talloc_strdup(d, "cups6.ppd");
+       d->info8.config_file            = talloc_strdup(d, "cupsui6.dll");
+       d->local.environment            = talloc_strdup(d, "Windows NT x86");
+       d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+       d->ex                           = true;
+
+       torture_assert(tctx,
+               fillup_printserver_info(tctx, p, d),
+               "failed to fillup printserver info");
+
+       if (!directory_exist(d->local.driver_directory)) {
+               torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+       }
+
+       torture_assert(tctx,
+               upload_printer_driver(tctx, dcerpc_server_name(p), d),
+               "failed to upload printer driver");
+
+       info8 = d->info8;
+       info8.architecture      = d->local.environment;
+
+       for (i=0; i < 3; i++) {
+               info8.driver_name               = talloc_asprintf(d, "torture_test_driver_%d", i);
+
+               torture_assert(tctx,
+                       test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &info8, add_flags, true, NULL),
+                       "failed to add driver");
+       }
+
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_0", info8.architecture, delete_flags, info8.version),
+               "failed to delete driver");
+
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_1", NULL),
+               "torture_test_driver_1 no longer on the server");
+
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_2", NULL),
+               "torture_test_driver_2 no longer on the server");
+
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_1", info8.architecture, delete_flags, info8.version),
+               "failed to delete driver");
+
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_2", NULL),
+               "torture_test_driver_2 no longer on the server");
+
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_2", info8.architecture, delete_flags, info8.version),
+               "failed to delete driver");
+
+       torture_assert(tctx,
+               remove_printer_driver(tctx, dcerpc_server_name(p), d),
+               "failed to remove printer driver");
+
+       return true;
+}
+
+static bool test_del_driver_all_files(struct torture_context *tctx,
+                                     struct dcerpc_pipe *p)
+{
+       struct torture_driver_context *d;
+       struct spoolss_StringArray *a;
+       uint32_t add_flags = APD_COPY_NEW_FILES;
+       uint32_t delete_flags = DPD_DELETE_ALL_FILES;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+       d = talloc_zero(tctx, struct torture_driver_context);
+
+       d->ex                           = true;
+       d->info8.version                = SPOOLSS_DRIVER_VERSION_200X;
+       d->info8.driver_name            = TORTURE_DRIVER_DELETER;
+       d->info8.architecture           = NULL;
+       d->info8.driver_path            = talloc_strdup(d, "pscript5.dll");
+       d->info8.data_file              = talloc_strdup(d, "cups6.ppd");
+       d->info8.config_file            = talloc_strdup(d, "cupsui6.dll");
+       d->info8.help_file              = talloc_strdup(d, "pscript.hlp");
+       d->local.environment            = talloc_strdup(d, SPOOLSS_ARCHITECTURE_x64);
+       d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/x64");
+
+       a                               = talloc_zero(d, struct spoolss_StringArray);
+       a->string                       = talloc_zero_array(a, const char *, 3);
+       a->string[0]                    = talloc_strdup(a->string, "cups6.inf");
+       a->string[1]                    = talloc_strdup(a->string, "cups6.ini");
+
+       d->info8.dependent_files        = a;
+       d->info8.architecture           = d->local.environment;
+
+       torture_assert(tctx,
+               fillup_printserver_info(tctx, p, d),
+               "failed to fillup printserver info");
+
+       if (!directory_exist(d->local.driver_directory)) {
+               torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+       }
+
+       torture_assert(tctx,
+               upload_printer_driver(tctx, dcerpc_server_name(p), d),
+               "failed to upload printer driver");
+
+       torture_assert(tctx,
+               test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d->info8, add_flags, true, NULL),
+               "failed to add driver");
+
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx(tctx, b, server_name_slash,
+                                          d->info8.driver_name,
+                                          d->local.environment,
+                                          delete_flags,
+                                          d->info8.version),
+               "failed to delete driver");
+
+       torture_assert(tctx,
+               check_printer_driver_files(tctx, dcerpc_server_name(p), d, false),
+               "printer driver file check failed");
+
+       talloc_free(d);
+       return true;
+}
+
+static bool test_del_driver_unused_files(struct torture_context *tctx,
+                                        struct dcerpc_pipe *p)
+{
+       struct torture_driver_context *d1;
+       struct torture_driver_context *d2;
+       uint32_t add_flags = APD_COPY_NEW_FILES;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+       d1 = talloc_zero(tctx, struct torture_driver_context);
+       d1->ex                          = true;
+       d1->info8.version               = SPOOLSS_DRIVER_VERSION_200X;
+       d1->info8.driver_name           = TORTURE_DRIVER_DELETER;
+       d1->info8.architecture          = NULL;
+       d1->info8.driver_path           = talloc_strdup(d1, "pscript5.dll");
+       d1->info8.data_file             = talloc_strdup(d1, "cups6.ppd");
+       d1->info8.config_file           = talloc_strdup(d1, "cupsui6.dll");
+       d1->info8.help_file             = talloc_strdup(d1, "pscript.hlp");
+       d1->local.environment           = talloc_strdup(d1, SPOOLSS_ARCHITECTURE_x64);
+       d1->local.driver_directory      = talloc_strdup(d1, "/usr/share/cups/drivers/x64");
+       d1->info8.architecture          = d1->local.environment;
+
+       d2 = talloc_zero(tctx, struct torture_driver_context);
+       d2->ex                          = true;
+       d2->info8.version               = SPOOLSS_DRIVER_VERSION_200X;
+       d2->info8.driver_name           = TORTURE_DRIVER_DELETERIN;
+       d2->info8.architecture          = NULL;
+       d2->info8.driver_path           = talloc_strdup(d2, "pscript5.dll");    /* overlapping */
+       d2->info8.data_file             = talloc_strdup(d2, "cupsps6.dll");
+       d2->info8.config_file           = talloc_strdup(d2, "cups6.ini");
+       d2->info8.help_file             = talloc_strdup(d2, "pscript.hlp");     /* overlapping */
+       d2->local.environment           = talloc_strdup(d2, SPOOLSS_ARCHITECTURE_x64);
+       d2->local.driver_directory      = talloc_strdup(d2, "/usr/share/cups/drivers/x64");
+       d2->info8.architecture          = d2->local.environment;
+
+       torture_assert(tctx,
+               fillup_printserver_info(tctx, p, d1),
+               "failed to fillup printserver info");
+       torture_assert(tctx,
+               fillup_printserver_info(tctx, p, d2),
+               "failed to fillup printserver info");
+
+       if (!directory_exist(d1->local.driver_directory)) {
+               torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+       }
+
+       torture_assert(tctx,
+               upload_printer_driver(tctx, dcerpc_server_name(p), d1),
+               "failed to upload printer driver");
+       torture_assert(tctx,
+               test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d1->info8, add_flags, true, NULL),
+               "failed to add driver");
+
+       torture_assert(tctx,
+               upload_printer_driver(tctx, dcerpc_server_name(p), d2),
+               "failed to upload printer driver");
+       torture_assert(tctx,
+               test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d2->info8, add_flags, true, NULL),
+               "failed to add driver");
+
+       /* some files are in use by a separate driver, should fail */
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash,
+                                              d1->info8.driver_name,
+                                              d1->local.environment,
+                                              DPD_DELETE_ALL_FILES,
+                                              d1->info8.version,
+                                              WERR_PRINTER_DRIVER_IN_USE),
+               "invalid delete driver response");
+
+       /* should only delete files not in use by other driver */
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash,
+                                              d1->info8.driver_name,
+                                              d1->local.environment,
+                                              DPD_DELETE_UNUSED_FILES,
+                                              d1->info8.version,
+                                              WERR_OK),
+               "failed to delete driver (unused files)");
+
+       /* check non-overlapping were deleted */
+       d1->info8.driver_path = NULL;
+       d1->info8.help_file = NULL;
+       torture_assert(tctx,
+               check_printer_driver_files(tctx, dcerpc_server_name(p), d1, false),
+               "printer driver file check failed");
+       /* d2 files should be uneffected */
+       torture_assert(tctx,
+               check_printer_driver_files(tctx, dcerpc_server_name(p), d2, true),
+               "printer driver file check failed");
+
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash,
+                                              d2->info8.driver_name,
+                                              d2->local.environment,
+                                              DPD_DELETE_ALL_FILES,
+                                              d2->info8.version,
+                                              WERR_OK),
+               "failed to delete driver");
+
+       torture_assert(tctx,
+               check_printer_driver_files(tctx, dcerpc_server_name(p), d2, false),
+               "printer driver file check failed");
+
+       talloc_free(d1);
+       talloc_free(d2);
+       return true;
+}
+
 struct torture_suite *torture_rpc_spoolss_driver(TALLOC_CTX *mem_ctx)
 {
        struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss.driver");
@@ -9178,5 +9725,13 @@ struct torture_suite *torture_rpc_spoolss_driver(TALLOC_CTX *mem_ctx)
 
        torture_rpc_tcase_add_test(tcase, "add_driver_adobe_cupsaddsmb", test_add_driver_adobe_cupsaddsmb);
 
+       torture_rpc_tcase_add_test(tcase, "add_driver_timestamps", test_add_driver_timestamps);
+
+       torture_rpc_tcase_add_test(tcase, "multiple_drivers", test_multiple_drivers);
+
+       torture_rpc_tcase_add_test(tcase, "del_driver_all_files", test_del_driver_all_files);
+
+       torture_rpc_tcase_add_test(tcase, "del_driver_unused_files", test_del_driver_unused_files);
+
        return suite;
 }