wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMB trans2 alias scanner
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "torture/torture.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/util.h"
|
||||
|
||||
int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname);
|
||||
|
||||
struct trans2_blobs {
|
||||
struct trans2_blobs *next, *prev;
|
||||
uint16_t level;
|
||||
DATA_BLOB params, data;
|
||||
};
|
||||
|
||||
/* look for aliases for a query */
|
||||
static bool gen_aliases(struct torture_context *tctx,
|
||||
struct smbcli_state *cli, struct smb_trans2 *t2,
|
||||
int level_offset)
|
||||
{
|
||||
uint16_t level;
|
||||
struct trans2_blobs *alias_blobs = NULL;
|
||||
struct trans2_blobs *t2b, *t2b2;
|
||||
int count=0, alias_count=0;
|
||||
|
||||
for (level=0;level<2000;level++) {
|
||||
NTSTATUS status;
|
||||
|
||||
SSVAL(t2->in.params.data, level_offset, level);
|
||||
|
||||
status = smb_raw_trans2(cli->tree, tctx, t2);
|
||||
if (!NT_STATUS_IS_OK(status)) continue;
|
||||
|
||||
t2b = talloc(tctx, struct trans2_blobs);
|
||||
t2b->level = level;
|
||||
t2b->params = t2->out.params;
|
||||
t2b->data = t2->out.data;
|
||||
DLIST_ADD(alias_blobs, t2b);
|
||||
torture_comment(tctx,
|
||||
"\tFound level %4u (0x%03x) of size %3d (0x%02x)\n",
|
||||
level, level,
|
||||
(int)t2b->data.length, (int)t2b->data.length);
|
||||
count++;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Found %d levels with success status\n", count);
|
||||
|
||||
for (t2b=alias_blobs; t2b; t2b=t2b->next) {
|
||||
for (t2b2=alias_blobs; t2b2; t2b2=t2b2->next) {
|
||||
if (t2b->level >= t2b2->level) continue;
|
||||
if (data_blob_equal(&t2b->params, &t2b2->params) &&
|
||||
data_blob_equal(&t2b->data, &t2b2->data)) {
|
||||
torture_comment(tctx,
|
||||
"\tLevel %u (0x%x) and level %u (0x%x) are possible aliases\n",
|
||||
t2b->level, t2b->level, t2b2->level, t2b2->level);
|
||||
alias_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Found %d aliased levels\n", alias_count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* look for qfsinfo aliases */
|
||||
static bool qfsinfo_aliases(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
struct smb_trans2 t2;
|
||||
uint16_t setup = TRANSACT2_QFSINFO;
|
||||
|
||||
t2.in.max_param = 0;
|
||||
t2.in.max_data = smb_raw_max_trans_data(cli->tree, 0);
|
||||
t2.in.max_setup = 0;
|
||||
t2.in.flags = 0;
|
||||
t2.in.timeout = 0;
|
||||
t2.in.setup_count = 1;
|
||||
t2.in.setup = &setup;
|
||||
t2.in.params = data_blob(NULL, 2);
|
||||
t2.in.data = data_blob(NULL, 0);
|
||||
|
||||
return gen_aliases(tctx, cli, &t2, 0);
|
||||
}
|
||||
|
||||
/* look for qfileinfo aliases */
|
||||
static bool qfileinfo_aliases(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
struct smb_trans2 t2;
|
||||
uint16_t setup = TRANSACT2_QFILEINFO;
|
||||
const char *fname = "\\qfileinfo_aliases.txt";
|
||||
int fnum;
|
||||
|
||||
t2.in.max_param = 2;
|
||||
t2.in.max_data = smb_raw_max_trans_data(cli->tree, 2);
|
||||
t2.in.max_setup = 0;
|
||||
t2.in.flags = 0;
|
||||
t2.in.timeout = 0;
|
||||
t2.in.setup_count = 1;
|
||||
t2.in.setup = &setup;
|
||||
t2.in.params = data_blob(NULL, 4);
|
||||
t2.in.data = data_blob(NULL, 0);
|
||||
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
fnum = create_complex_file(cli, cli, fname);
|
||||
torture_assert(tctx, fnum != -1, talloc_asprintf(tctx,
|
||||
"open of %s failed (%s)", fname,
|
||||
smbcli_errstr(cli->tree)));
|
||||
|
||||
smbcli_write(cli->tree, fnum, 0, &t2, 0, sizeof(t2));
|
||||
|
||||
SSVAL(t2.in.params.data, 0, fnum);
|
||||
|
||||
if (!gen_aliases(tctx, cli, &t2, 2))
|
||||
return false;
|
||||
|
||||
smbcli_close(cli->tree, fnum);
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* look for qpathinfo aliases */
|
||||
static bool qpathinfo_aliases(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
struct smb_trans2 t2;
|
||||
uint16_t setup = TRANSACT2_QPATHINFO;
|
||||
const char *fname = "\\qpathinfo_aliases.txt";
|
||||
int fnum;
|
||||
|
||||
t2.in.max_param = 2;
|
||||
t2.in.max_data = smb_raw_max_trans_data(cli->tree, 2);
|
||||
t2.in.max_setup = 0;
|
||||
t2.in.flags = 0;
|
||||
t2.in.timeout = 0;
|
||||
t2.in.setup_count = 1;
|
||||
t2.in.setup = &setup;
|
||||
t2.in.params = data_blob_talloc(tctx, NULL, 6);
|
||||
t2.in.data = data_blob(NULL, 0);
|
||||
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
fnum = create_complex_file(cli, cli, fname);
|
||||
torture_assert(tctx, fnum != -1, talloc_asprintf(tctx,
|
||||
"open of %s failed (%s)", fname,
|
||||
smbcli_errstr(cli->tree)));
|
||||
|
||||
smbcli_write(cli->tree, fnum, 0, &t2, 0, sizeof(t2));
|
||||
smbcli_close(cli->tree, fnum);
|
||||
|
||||
SIVAL(t2.in.params.data, 2, 0);
|
||||
|
||||
smbcli_blob_append_string(cli->session, tctx, &t2.in.params,
|
||||
fname, STR_TERMINATE);
|
||||
|
||||
if (!gen_aliases(tctx, cli, &t2, 0))
|
||||
return false;
|
||||
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* look for trans2 findfirst aliases */
|
||||
static bool findfirst_aliases(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
struct smb_trans2 t2;
|
||||
uint16_t setup = TRANSACT2_FINDFIRST;
|
||||
const char *fname = "\\findfirst_aliases.txt";
|
||||
int fnum;
|
||||
|
||||
t2.in.max_param = 16;
|
||||
t2.in.max_data = smb_raw_max_trans_data(cli->tree, 16);
|
||||
t2.in.max_setup = 0;
|
||||
t2.in.flags = 0;
|
||||
t2.in.timeout = 0;
|
||||
t2.in.setup_count = 1;
|
||||
t2.in.setup = &setup;
|
||||
t2.in.params = data_blob_talloc(tctx, NULL, 12);
|
||||
t2.in.data = data_blob(NULL, 0);
|
||||
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
fnum = create_complex_file(cli, cli, fname);
|
||||
torture_assert(tctx, fnum != -1, talloc_asprintf(tctx,
|
||||
"open of %s failed (%s)", fname,
|
||||
smbcli_errstr(cli->tree)));
|
||||
|
||||
smbcli_write(cli->tree, fnum, 0, &t2, 0, sizeof(t2));
|
||||
smbcli_close(cli->tree, fnum);
|
||||
|
||||
SSVAL(t2.in.params.data, 0, 0);
|
||||
SSVAL(t2.in.params.data, 2, 1);
|
||||
SSVAL(t2.in.params.data, 4, FLAG_TRANS2_FIND_CLOSE);
|
||||
SSVAL(t2.in.params.data, 6, 0);
|
||||
SIVAL(t2.in.params.data, 8, 0);
|
||||
|
||||
smbcli_blob_append_string(cli->session, tctx, &t2.in.params,
|
||||
fname, STR_TERMINATE);
|
||||
|
||||
if (!gen_aliases(tctx, cli, &t2, 6))
|
||||
return false;
|
||||
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* look for aliases for a set function */
|
||||
static bool gen_set_aliases(struct torture_context *tctx,
|
||||
struct smbcli_state *cli,
|
||||
struct smb_trans2 *t2, int level_offset)
|
||||
{
|
||||
uint16_t level;
|
||||
struct trans2_blobs *alias_blobs = NULL;
|
||||
struct trans2_blobs *t2b;
|
||||
int count=0, dsize;
|
||||
|
||||
for (level=1;level<1100;level++) {
|
||||
NTSTATUS status, status1;
|
||||
SSVAL(t2->in.params.data, level_offset, level);
|
||||
|
||||
status1 = NT_STATUS_OK;
|
||||
|
||||
for (dsize=2; dsize<1024; dsize += 2) {
|
||||
data_blob_free(&t2->in.data);
|
||||
t2->in.data = data_blob(NULL, dsize);
|
||||
data_blob_clear(&t2->in.data);
|
||||
status = smb_raw_trans2(cli->tree, tctx, t2);
|
||||
/* some error codes mean that this whole level doesn't exist */
|
||||
if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, status) ||
|
||||
NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, status) ||
|
||||
NT_STATUS_EQUAL(NT_STATUS_NOT_SUPPORTED, status)) {
|
||||
break;
|
||||
}
|
||||
if (NT_STATUS_IS_OK(status)) break;
|
||||
|
||||
/* invalid parameter means that the level exists at this
|
||||
size, but the contents are wrong (not surprising with
|
||||
all zeros!) */
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) break;
|
||||
|
||||
/* this is the usual code for 'wrong size' */
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_INFO_LENGTH_MISMATCH)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_EQUAL(status, status1)) {
|
||||
torture_comment(tctx, "level=%d size=%d %s\n", level, dsize, nt_errstr(status));
|
||||
}
|
||||
status1 = status;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(status) &&
|
||||
!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) continue;
|
||||
|
||||
t2b = talloc(tctx, struct trans2_blobs);
|
||||
t2b->level = level;
|
||||
t2b->params = t2->out.params;
|
||||
t2b->data = t2->out.data;
|
||||
DLIST_ADD(alias_blobs, t2b);
|
||||
torture_comment(tctx,
|
||||
"\tFound level %4u (0x%03x) of size %3d (0x%02x)\n",
|
||||
level, level,
|
||||
(int)t2->in.data.length, (int)t2->in.data.length);
|
||||
count++;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Found %d valid levels\n", count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* look for setfileinfo aliases */
|
||||
static bool setfileinfo_aliases(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
struct smb_trans2 t2;
|
||||
uint16_t setup = TRANSACT2_SETFILEINFO;
|
||||
const char *fname = "\\setfileinfo_aliases.txt";
|
||||
int fnum;
|
||||
|
||||
t2.in.max_param = 2;
|
||||
t2.in.max_data = 0;
|
||||
t2.in.max_setup = 0;
|
||||
t2.in.flags = 0;
|
||||
t2.in.timeout = 0;
|
||||
t2.in.setup_count = 1;
|
||||
t2.in.setup = &setup;
|
||||
t2.in.params = data_blob(NULL, 6);
|
||||
t2.in.data = data_blob(NULL, 0);
|
||||
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
fnum = create_complex_file(cli, cli, fname);
|
||||
torture_assert(tctx, fnum != -1, talloc_asprintf(tctx,
|
||||
"open of %s failed (%s)", fname,
|
||||
smbcli_errstr(cli->tree)));
|
||||
|
||||
smbcli_write(cli->tree, fnum, 0, &t2, 0, sizeof(t2));
|
||||
|
||||
SSVAL(t2.in.params.data, 0, fnum);
|
||||
SSVAL(t2.in.params.data, 4, 0);
|
||||
|
||||
gen_set_aliases(tctx, cli, &t2, 2);
|
||||
|
||||
smbcli_close(cli->tree, fnum);
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* look for setpathinfo aliases */
|
||||
static bool setpathinfo_aliases(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
struct smb_trans2 t2;
|
||||
uint16_t setup = TRANSACT2_SETPATHINFO;
|
||||
const char *fname = "\\setpathinfo_aliases.txt";
|
||||
int fnum;
|
||||
|
||||
t2.in.max_param = 32;
|
||||
t2.in.max_data = smb_raw_max_trans_data(cli->tree, 32);
|
||||
t2.in.max_setup = 0;
|
||||
t2.in.flags = 0;
|
||||
t2.in.timeout = 0;
|
||||
t2.in.setup_count = 1;
|
||||
t2.in.setup = &setup;
|
||||
t2.in.params = data_blob_talloc(tctx, NULL, 4);
|
||||
t2.in.data = data_blob(NULL, 0);
|
||||
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
|
||||
fnum = create_complex_file(cli, cli, fname);
|
||||
torture_assert(tctx, fnum != -1, talloc_asprintf(tctx,
|
||||
"open of %s failed (%s)", fname,
|
||||
smbcli_errstr(cli->tree)));
|
||||
|
||||
smbcli_write(cli->tree, fnum, 0, &t2, 0, sizeof(t2));
|
||||
smbcli_close(cli->tree, fnum);
|
||||
|
||||
SSVAL(t2.in.params.data, 2, 0);
|
||||
|
||||
smbcli_blob_append_string(cli->session, tctx, &t2.in.params,
|
||||
fname, STR_TERMINATE);
|
||||
|
||||
if (!gen_set_aliases(tctx, cli, &t2, 0))
|
||||
return false;
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli->tree, fname),
|
||||
talloc_asprintf(tctx, "unlink: %s", smbcli_errstr(cli->tree)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* look for aliased info levels in trans2 calls */
|
||||
struct torture_suite *torture_trans2_aliases(void)
|
||||
{
|
||||
struct torture_suite *suite = torture_suite_create(
|
||||
talloc_autofree_context(),
|
||||
"ALIASES");
|
||||
|
||||
torture_suite_add_1smb_test(suite, "QFILEINFO aliases",
|
||||
qfsinfo_aliases);
|
||||
torture_suite_add_1smb_test(suite, "QFSINFO aliases", qfileinfo_aliases);
|
||||
torture_suite_add_1smb_test(suite, "QPATHINFO aliases", qpathinfo_aliases);
|
||||
torture_suite_add_1smb_test(suite, "FINDFIRST aliases", findfirst_aliases);
|
||||
torture_suite_add_1smb_test(suite, "setfileinfo_aliases",
|
||||
setfileinfo_aliases);
|
||||
torture_suite_add_1smb_test(suite, "setpathinfo_aliases",
|
||||
setpathinfo_aliases);
|
||||
|
||||
return suite;
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
openattr tester
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "torture/torture.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/util.h"
|
||||
|
||||
extern int torture_failures;
|
||||
|
||||
#define CHECK_MAX_FAILURES(label) do { if (++failures >= torture_failures) goto label; } while (0)
|
||||
|
||||
|
||||
static const uint32_t open_attrs_table[] = {
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
FILE_ATTRIBUTE_ARCHIVE,
|
||||
FILE_ATTRIBUTE_READONLY,
|
||||
FILE_ATTRIBUTE_HIDDEN,
|
||||
FILE_ATTRIBUTE_SYSTEM,
|
||||
|
||||
FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY,
|
||||
FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN,
|
||||
FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM,
|
||||
FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN,
|
||||
FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM,
|
||||
FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
|
||||
|
||||
FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN,
|
||||
FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM,
|
||||
FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
|
||||
FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_SYSTEM,
|
||||
};
|
||||
|
||||
struct trunc_open_results {
|
||||
uint_t num;
|
||||
uint32_t init_attr;
|
||||
uint32_t trunc_attr;
|
||||
uint32_t result_attr;
|
||||
};
|
||||
|
||||
static const struct trunc_open_results attr_results[] = {
|
||||
{ 0, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE },
|
||||
{ 1, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE },
|
||||
{ 2, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY },
|
||||
{ 16, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE },
|
||||
{ 17, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE },
|
||||
{ 18, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY },
|
||||
{ 51, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
|
||||
{ 54, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
|
||||
{ 56, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
|
||||
{ 68, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
|
||||
{ 71, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
|
||||
{ 73, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM },
|
||||
{ 99, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
|
||||
{ 102, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
|
||||
{ 104, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
|
||||
{ 116, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
|
||||
{ 119, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
|
||||
{ 121, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM },
|
||||
{ 170, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN },
|
||||
{ 173, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM },
|
||||
{ 227, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
|
||||
{ 230, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
|
||||
{ 232, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
|
||||
{ 244, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
|
||||
{ 247, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
|
||||
{ 249, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM }
|
||||
};
|
||||
|
||||
|
||||
BOOL torture_openattrtest(struct torture_context *tctx,
|
||||
struct smbcli_state *cli1)
|
||||
{
|
||||
const char *fname = "\\openattr.file";
|
||||
int fnum1;
|
||||
uint16_t attr;
|
||||
uint_t i, j, k, l;
|
||||
int failures = 0;
|
||||
|
||||
for (k = 0, i = 0; i < sizeof(open_attrs_table)/sizeof(uint32_t); i++) {
|
||||
smbcli_setatr(cli1->tree, fname, 0, 0);
|
||||
smbcli_unlink(cli1->tree, fname);
|
||||
fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0,
|
||||
SEC_FILE_WRITE_DATA,
|
||||
open_attrs_table[i],
|
||||
NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
|
||||
|
||||
torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open %d (1) of %s failed (%s)", i,
|
||||
fname, smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx,
|
||||
smbcli_close(cli1->tree, fnum1),
|
||||
talloc_asprintf(tctx, "close %d (1) of %s failed (%s)", i, fname,
|
||||
smbcli_errstr(cli1->tree)));
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(open_attrs_table); j++) {
|
||||
fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0,
|
||||
SEC_FILE_READ_DATA|
|
||||
SEC_FILE_WRITE_DATA,
|
||||
open_attrs_table[j],
|
||||
NTCREATEX_SHARE_ACCESS_NONE,
|
||||
NTCREATEX_DISP_OVERWRITE, 0, 0);
|
||||
|
||||
if (fnum1 == -1) {
|
||||
for (l = 0; l < ARRAY_SIZE(attr_results); l++) {
|
||||
if (attr_results[l].num == k) {
|
||||
torture_comment(tctx, "[%d] trunc open 0x%x -> 0x%x of %s failed - should have succeeded !(%s)\n",
|
||||
k, open_attrs_table[i],
|
||||
open_attrs_table[j],
|
||||
fname, smbcli_errstr(cli1->tree));
|
||||
CHECK_MAX_FAILURES(error_exit);
|
||||
}
|
||||
}
|
||||
torture_assert_ntstatus_equal(tctx,
|
||||
smbcli_nt_error(cli1->tree), NT_STATUS_ACCESS_DENIED,
|
||||
talloc_asprintf(tctx, "[%d] trunc open 0x%x -> 0x%x failed with wrong error code %s",
|
||||
k, open_attrs_table[i], open_attrs_table[j],
|
||||
smbcli_errstr(cli1->tree)));
|
||||
CHECK_MAX_FAILURES(error_exit);
|
||||
#if 0
|
||||
torture_comment(tctx, "[%d] trunc open 0x%x -> 0x%x failed\n", k, open_attrs_table[i], open_attrs_table[j]);
|
||||
#endif
|
||||
k++;
|
||||
continue;
|
||||
}
|
||||
|
||||
torture_assert_ntstatus_ok(tctx,
|
||||
smbcli_close(cli1->tree, fnum1),
|
||||
talloc_asprintf(tctx, "close %d (2) of %s failed (%s)", j,
|
||||
fname, smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx,
|
||||
smbcli_getatr(cli1->tree, fname, &attr, NULL, NULL),
|
||||
talloc_asprintf(tctx, "getatr(2) failed (%s)", smbcli_errstr(cli1->tree)));
|
||||
|
||||
#if 0
|
||||
torture_comment(tctx, "[%d] getatr check [0x%x] trunc [0x%x] got attr 0x%x\n",
|
||||
k, open_attrs_table[i], open_attrs_table[j], attr );
|
||||
#endif
|
||||
|
||||
for (l = 0; l < ARRAY_SIZE(attr_results); l++) {
|
||||
if (attr_results[l].num == k) {
|
||||
if (attr != attr_results[l].result_attr ||
|
||||
open_attrs_table[i] != attr_results[l].init_attr ||
|
||||
open_attrs_table[j] != attr_results[l].trunc_attr) {
|
||||
torture_comment(tctx, "[%d] getatr check failed. [0x%x] trunc [0x%x] got attr 0x%x, should be 0x%x\n",
|
||||
k, open_attrs_table[i],
|
||||
open_attrs_table[j],
|
||||
(uint_t)attr,
|
||||
attr_results[l].result_attr);
|
||||
CHECK_MAX_FAILURES(error_exit);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
k++;
|
||||
}
|
||||
}
|
||||
error_exit:
|
||||
smbcli_setatr(cli1->tree, fname, 0, 0);
|
||||
smbcli_unlink(cli1->tree, fname);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB torture tester - charset test routines
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "torture/torture.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/util.h"
|
||||
|
||||
#define BASEDIR "\\chartest\\"
|
||||
|
||||
/*
|
||||
open a file using a set of unicode code points for the name
|
||||
|
||||
the prefix BASEDIR is added before the name
|
||||
*/
|
||||
static NTSTATUS unicode_open(struct torture_context *tctx,
|
||||
struct smbcli_tree *tree,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
uint32_t open_disposition,
|
||||
const uint32_t *u_name,
|
||||
size_t u_name_len)
|
||||
{
|
||||
union smb_open io;
|
||||
char *fname, *fname2=NULL, *ucs_name;
|
||||
int i;
|
||||
NTSTATUS status;
|
||||
|
||||
ucs_name = talloc_size(mem_ctx, (1+u_name_len)*2);
|
||||
if (!ucs_name) {
|
||||
printf("Failed to create UCS2 Name - talloc() failure\n");
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (i=0;i<u_name_len;i++) {
|
||||
SSVAL(ucs_name, i*2, u_name[i]);
|
||||
}
|
||||
SSVAL(ucs_name, i*2, 0);
|
||||
|
||||
i = convert_string_talloc(ucs_name, CH_UTF16, CH_UNIX, ucs_name, (1+u_name_len)*2, (void **)&fname);
|
||||
if (i == -1) {
|
||||
torture_comment(tctx, "Failed to convert UCS2 Name into unix - convert_string_talloc() failure\n");
|
||||
talloc_free(ucs_name);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
fname2 = talloc_asprintf(ucs_name, "%s%s", BASEDIR, fname);
|
||||
if (!fname2) {
|
||||
talloc_free(ucs_name);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
io.generic.level = RAW_OPEN_NTCREATEX;
|
||||
io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
|
||||
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_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 = fname2;
|
||||
io.ntcreatex.in.open_disposition = open_disposition;
|
||||
|
||||
status = smb_raw_open(tree, mem_ctx, &io);
|
||||
|
||||
talloc_free(ucs_name);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
see if the server recognises composed characters
|
||||
*/
|
||||
static BOOL test_composed(struct torture_context *tctx,
|
||||
struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
const uint32_t name1[] = {0x61, 0x308};
|
||||
const uint32_t name2[] = {0xe4};
|
||||
NTSTATUS status1, status2;
|
||||
|
||||
printf("Testing composite character (a umlaut)\n");
|
||||
|
||||
status1 = unicode_open(tctx, cli->tree, mem_ctx, NTCREATEX_DISP_CREATE, name1, 2);
|
||||
if (!NT_STATUS_IS_OK(status1)) {
|
||||
printf("Failed to create composed name - %s\n",
|
||||
nt_errstr(status1));
|
||||
return False;
|
||||
}
|
||||
|
||||
status2 = unicode_open(tctx, cli->tree, mem_ctx, NTCREATEX_DISP_CREATE, name2, 1);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status2)) {
|
||||
printf("Failed to create accented character - %s\n",
|
||||
nt_errstr(status2));
|
||||
return False;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
see if the server recognises a naked diacritical
|
||||
*/
|
||||
static BOOL test_diacritical(struct torture_context *tctx,
|
||||
struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
const uint32_t name1[] = {0x308};
|
||||
const uint32_t name2[] = {0x308, 0x308};
|
||||
NTSTATUS status1, status2;
|
||||
|
||||
printf("Testing naked diacritical (umlaut)\n");
|
||||
|
||||
status1 = unicode_open(tctx, cli->tree, mem_ctx, NTCREATEX_DISP_CREATE, name1, 1);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status1)) {
|
||||
printf("Failed to create naked diacritical - %s\n",
|
||||
nt_errstr(status1));
|
||||
return False;
|
||||
}
|
||||
|
||||
/* try a double diacritical */
|
||||
status2 = unicode_open(tctx, cli->tree, mem_ctx, NTCREATEX_DISP_CREATE, name2, 2);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status2)) {
|
||||
printf("Failed to create double naked diacritical - %s\n",
|
||||
nt_errstr(status2));
|
||||
return False;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
see if the server recognises a partial surrogate pair
|
||||
*/
|
||||
static BOOL test_surrogate(struct torture_context *tctx,
|
||||
struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
const uint32_t name1[] = {0xd800};
|
||||
const uint32_t name2[] = {0xdc00};
|
||||
const uint32_t name3[] = {0xd800, 0xdc00};
|
||||
NTSTATUS status;
|
||||
|
||||
printf("Testing partial surrogate\n");
|
||||
|
||||
status = unicode_open(tctx, cli->tree, mem_ctx, NTCREATEX_DISP_CREATE, name1, 1);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
printf("Failed to create partial surrogate 1 - %s\n",
|
||||
nt_errstr(status));
|
||||
return False;
|
||||
}
|
||||
|
||||
status = unicode_open(tctx, cli->tree, mem_ctx, NTCREATEX_DISP_CREATE, name2, 1);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
printf("Failed to create partial surrogate 2 - %s\n",
|
||||
nt_errstr(status));
|
||||
return False;
|
||||
}
|
||||
|
||||
status = unicode_open(tctx, cli->tree, mem_ctx, NTCREATEX_DISP_CREATE, name3, 2);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
printf("Failed to create full surrogate - %s\n",
|
||||
nt_errstr(status));
|
||||
return False;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
see if the server recognises wide-a characters
|
||||
*/
|
||||
static BOOL test_widea(struct torture_context *tctx,
|
||||
struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
const uint32_t name1[] = {'a'};
|
||||
const uint32_t name2[] = {0xff41};
|
||||
const uint32_t name3[] = {0xff21};
|
||||
NTSTATUS status;
|
||||
|
||||
printf("Testing wide-a\n");
|
||||
|
||||
status = unicode_open(tctx, cli->tree, mem_ctx, NTCREATEX_DISP_CREATE, name1, 1);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
printf("Failed to create 'a' - %s\n",
|
||||
nt_errstr(status));
|
||||
return False;
|
||||
}
|
||||
|
||||
status = unicode_open(tctx, cli->tree, mem_ctx, NTCREATEX_DISP_CREATE, name2, 1);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
printf("Failed to create wide-a - %s\n",
|
||||
nt_errstr(status));
|
||||
return False;
|
||||
}
|
||||
|
||||
status = unicode_open(tctx, cli->tree, mem_ctx, NTCREATEX_DISP_CREATE, name3, 1);
|
||||
|
||||
if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
|
||||
printf("Expected %s creating wide-A - %s\n",
|
||||
nt_errstr(NT_STATUS_OBJECT_NAME_COLLISION),
|
||||
nt_errstr(status));
|
||||
return False;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
BOOL torture_charset(struct torture_context *tctx, struct smbcli_state *cli)
|
||||
{
|
||||
BOOL ret = True;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
mem_ctx = talloc_init("torture_charset");
|
||||
|
||||
if (!torture_setup_dir(cli, BASEDIR)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (!test_composed(tctx, cli, mem_ctx)) {
|
||||
ret = False;
|
||||
}
|
||||
|
||||
if (!test_diacritical(tctx, cli, mem_ctx)) {
|
||||
ret = False;
|
||||
}
|
||||
|
||||
if (!test_surrogate(tctx, cli, mem_ctx)) {
|
||||
ret = False;
|
||||
}
|
||||
|
||||
if (!test_widea(tctx, cli, mem_ctx)) {
|
||||
ret = False;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
test suite for delayed write update
|
||||
|
||||
Copyright (C) Volker Lendecke 2004
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
Copyright (C) Jeremy Allison 2004
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "torture/torture.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "system/time.h"
|
||||
#include "system/filesys.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/util.h"
|
||||
|
||||
#define BASEDIR "\\delaywrite"
|
||||
|
||||
static BOOL test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli)
|
||||
{
|
||||
union smb_fileinfo finfo1, finfo2;
|
||||
const char *fname = BASEDIR "\\torture_file.txt";
|
||||
NTSTATUS status;
|
||||
int fnum1 = -1;
|
||||
BOOL ret = True;
|
||||
ssize_t written;
|
||||
time_t t;
|
||||
|
||||
if (!torture_setup_dir(cli, BASEDIR)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
|
||||
if (fnum1 == -1) {
|
||||
torture_comment(tctx, "Failed to open %s\n", fname);
|
||||
return False;
|
||||
}
|
||||
|
||||
finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
|
||||
finfo1.basic_info.in.file.fnum = fnum1;
|
||||
finfo2 = finfo1;
|
||||
|
||||
status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
return False;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Initial write time %s\n",
|
||||
nt_time_string(tctx, finfo1.basic_info.out.write_time));
|
||||
|
||||
/* 3 second delay to ensure we get past any 2 second time
|
||||
granularity (older systems may have that) */
|
||||
sleep(3);
|
||||
|
||||
written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
|
||||
|
||||
if (written != 1) {
|
||||
torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
|
||||
(int)written, __location__);
|
||||
return False;
|
||||
}
|
||||
|
||||
t = time(NULL);
|
||||
|
||||
while (time(NULL) < t+120) {
|
||||
status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
ret = False;
|
||||
break;
|
||||
}
|
||||
torture_comment(tctx, "write time %s\n",
|
||||
nt_time_string(tctx, finfo2.basic_info.out.write_time));
|
||||
if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server updated write_time after %d seconds\n",
|
||||
(int)(time(NULL) - t));
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server did not update write time?!\n");
|
||||
ret = False;
|
||||
}
|
||||
|
||||
|
||||
if (fnum1 != -1)
|
||||
smbcli_close(cli->tree, fnum1);
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
smbcli_deltree(cli->tree, BASEDIR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do as above, but using 2 connections.
|
||||
*/
|
||||
|
||||
static BOOL test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli,
|
||||
struct smbcli_state *cli2)
|
||||
{
|
||||
union smb_fileinfo finfo1, finfo2;
|
||||
const char *fname = BASEDIR "\\torture_file.txt";
|
||||
NTSTATUS status;
|
||||
int fnum1 = -1;
|
||||
int fnum2 = -1;
|
||||
BOOL ret = True;
|
||||
ssize_t written;
|
||||
time_t t;
|
||||
union smb_flush flsh;
|
||||
|
||||
if (!torture_setup_dir(cli, BASEDIR)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
|
||||
if (fnum1 == -1) {
|
||||
torture_comment(tctx, "Failed to open %s\n", fname);
|
||||
return False;
|
||||
}
|
||||
|
||||
finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
|
||||
finfo1.basic_info.in.file.fnum = fnum1;
|
||||
finfo2 = finfo1;
|
||||
|
||||
status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
return False;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Initial write time %s\n",
|
||||
nt_time_string(tctx, finfo1.basic_info.out.write_time));
|
||||
|
||||
/* 3 second delay to ensure we get past any 2 second time
|
||||
granularity (older systems may have that) */
|
||||
sleep(3);
|
||||
|
||||
{
|
||||
/* Try using setfileinfo instead of write to update write time. */
|
||||
union smb_setfileinfo sfinfo;
|
||||
time_t t_set = time(NULL);
|
||||
sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
|
||||
sfinfo.basic_info.in.file.fnum = fnum1;
|
||||
sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
|
||||
sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
|
||||
|
||||
/* I tried this with both + and - ve to see if it makes a different.
|
||||
It doesn't - once the filetime is set via setfileinfo it stays that way. */
|
||||
#if 1
|
||||
unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
|
||||
#else
|
||||
unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
|
||||
#endif
|
||||
sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
|
||||
sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
|
||||
|
||||
status = smb_raw_setfileinfo(cli->tree, &sfinfo);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
t = time(NULL);
|
||||
|
||||
while (time(NULL) < t+120) {
|
||||
finfo2.basic_info.in.file.path = fname;
|
||||
|
||||
status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
ret = False;
|
||||
break;
|
||||
}
|
||||
torture_comment(tctx, "write time %s\n",
|
||||
nt_time_string(tctx, finfo2.basic_info.out.write_time));
|
||||
if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server updated write_time after %d seconds\n",
|
||||
(int)(time(NULL) - t));
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server did not update write time?!\n");
|
||||
ret = False;
|
||||
}
|
||||
|
||||
/* Now try a write to see if the write time gets reset. */
|
||||
|
||||
finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
|
||||
finfo1.basic_info.in.file.fnum = fnum1;
|
||||
finfo2 = finfo1;
|
||||
|
||||
status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
return False;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Modified write time %s\n",
|
||||
nt_time_string(tctx, finfo1.basic_info.out.write_time));
|
||||
|
||||
|
||||
torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
|
||||
|
||||
written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
|
||||
|
||||
if (written != 10) {
|
||||
torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
|
||||
(int)written, __location__);
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Just to prove to tridge that the an smbflush has no effect on
|
||||
the write time :-). The setfileinfo IS STICKY. JRA. */
|
||||
|
||||
torture_comment(tctx, "Doing flush after write\n");
|
||||
|
||||
flsh.flush.level = RAW_FLUSH_FLUSH;
|
||||
flsh.flush.in.file.fnum = fnum1;
|
||||
status = smb_raw_flush(cli->tree, &flsh);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
|
||||
return False;
|
||||
}
|
||||
|
||||
t = time(NULL);
|
||||
|
||||
/* Once the time was set using setfileinfo then it stays set - writes
|
||||
don't have any effect. But make sure. */
|
||||
|
||||
while (time(NULL) < t+15) {
|
||||
status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
ret = False;
|
||||
break;
|
||||
}
|
||||
torture_comment(tctx, "write time %s\n",
|
||||
nt_time_string(tctx, finfo2.basic_info.out.write_time));
|
||||
if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server updated write_time after %d seconds\n",
|
||||
(int)(time(NULL) - t));
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server did not update write time\n");
|
||||
}
|
||||
|
||||
fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
|
||||
if (fnum2 == -1) {
|
||||
torture_comment(tctx, "Failed to open %s\n", fname);
|
||||
return False;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
|
||||
|
||||
written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
|
||||
|
||||
if (written != 10) {
|
||||
torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
|
||||
(int)written, __location__);
|
||||
return False;
|
||||
}
|
||||
|
||||
status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
return False;
|
||||
}
|
||||
torture_comment(tctx, "write time %s\n",
|
||||
nt_time_string(tctx, finfo2.basic_info.out.write_time));
|
||||
if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server updated write_time\n");
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
|
||||
smbcli_close(cli->tree, fnum1);
|
||||
fnum1 = -1;
|
||||
|
||||
torture_comment(tctx, "Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
|
||||
|
||||
written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
|
||||
|
||||
if (written != 10) {
|
||||
torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
|
||||
(int)written, __location__);
|
||||
return False;
|
||||
}
|
||||
|
||||
finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
|
||||
finfo1.basic_info.in.file.fnum = fnum2;
|
||||
finfo2 = finfo1;
|
||||
status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
return False;
|
||||
}
|
||||
torture_comment(tctx, "write time %s\n",
|
||||
nt_time_string(tctx, finfo2.basic_info.out.write_time));
|
||||
if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server updated write_time\n");
|
||||
}
|
||||
|
||||
t = time(NULL);
|
||||
|
||||
/* Once the time was set using setfileinfo then it stays set - writes
|
||||
don't have any effect. But make sure. */
|
||||
|
||||
while (time(NULL) < t+15) {
|
||||
status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
ret = False;
|
||||
break;
|
||||
}
|
||||
torture_comment(tctx, "write time %s\n",
|
||||
nt_time_string(tctx, finfo2.basic_info.out.write_time));
|
||||
if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server updated write_time after %d seconds\n",
|
||||
(int)(time(NULL) - t));
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server did not update write time\n");
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Closing both fd's to see if write time updated.\n");
|
||||
|
||||
smbcli_close(cli->tree, fnum2);
|
||||
fnum2 = -1;
|
||||
|
||||
fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
|
||||
if (fnum1 == -1) {
|
||||
torture_comment(tctx, "Failed to open %s\n", fname);
|
||||
return False;
|
||||
}
|
||||
|
||||
finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
|
||||
finfo1.basic_info.in.file.fnum = fnum1;
|
||||
finfo2 = finfo1;
|
||||
|
||||
status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
return False;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Second open initial write time %s\n",
|
||||
nt_time_string(tctx, finfo1.basic_info.out.write_time));
|
||||
|
||||
sleep(10);
|
||||
torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
|
||||
|
||||
written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
|
||||
|
||||
if (written != 10) {
|
||||
torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
|
||||
(int)written, __location__);
|
||||
return False;
|
||||
}
|
||||
|
||||
finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
|
||||
finfo1.basic_info.in.file.fnum = fnum1;
|
||||
finfo2 = finfo1;
|
||||
status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
return False;
|
||||
}
|
||||
torture_comment(tctx, "write time %s\n",
|
||||
nt_time_string(tctx, finfo2.basic_info.out.write_time));
|
||||
if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server updated write_time\n");
|
||||
}
|
||||
|
||||
t = time(NULL);
|
||||
|
||||
/* Once the time was set using setfileinfo then it stays set - writes
|
||||
don't have any effect. But make sure. */
|
||||
|
||||
while (time(NULL) < t+15) {
|
||||
status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
ret = False;
|
||||
break;
|
||||
}
|
||||
torture_comment(tctx, "write time %s\n",
|
||||
nt_time_string(tctx, finfo2.basic_info.out.write_time));
|
||||
if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server updated write_time after %d seconds\n",
|
||||
(int)(time(NULL) - t));
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "Server did not update write time\n");
|
||||
}
|
||||
|
||||
|
||||
/* One more test to do. We should read the filetime via findfirst on the
|
||||
second connection to ensure it's the same. This is very easy for a Windows
|
||||
server but a bastard to get right on a POSIX server. JRA. */
|
||||
|
||||
if (fnum1 != -1)
|
||||
smbcli_close(cli->tree, fnum1);
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
smbcli_deltree(cli->tree, BASEDIR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Windows does obviously not update the stat info during a write call. I
|
||||
* *think* this is the problem causing a spurious Excel 2003 on XP error
|
||||
* message when saving a file. Excel does a setfileinfo, writes, and then does
|
||||
* a getpath(!)info. Or so... For Samba sometimes it displays an error message
|
||||
* that the file might have been changed in between. What i've been able to
|
||||
* trace down is that this happens if the getpathinfo after the write shows a
|
||||
* different last write time than the setfileinfo showed. This is really
|
||||
* nasty....
|
||||
*/
|
||||
|
||||
static BOOL test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli,
|
||||
struct smbcli_state *cli2)
|
||||
{
|
||||
union smb_fileinfo finfo1, finfo2;
|
||||
const char *fname = BASEDIR "\\torture_file.txt";
|
||||
NTSTATUS status;
|
||||
int fnum1 = -1;
|
||||
int fnum2;
|
||||
BOOL ret = True;
|
||||
ssize_t written;
|
||||
|
||||
if (!torture_setup_dir(cli, BASEDIR)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
|
||||
if (fnum1 == -1) {
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
|
||||
finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
|
||||
finfo1.basic_info.in.file.fnum = fnum1;
|
||||
|
||||
status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
|
||||
msleep(1000);
|
||||
|
||||
written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
|
||||
|
||||
if (written != 1) {
|
||||
torture_comment(tctx, "(%s) written gave %d - should have been 1\n",
|
||||
__location__, (int)written);
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
|
||||
fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
|
||||
if (fnum2 == -1) {
|
||||
torture_comment(tctx, "(%s) failed to open 2nd time - %s\n",
|
||||
__location__, smbcli_errstr(cli2->tree));
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
|
||||
written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
|
||||
|
||||
if (written != 1) {
|
||||
torture_comment(tctx, "(%s) written gave %d - should have been 1\n",
|
||||
__location__, (int)written);
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
|
||||
finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
|
||||
finfo2.basic_info.in.file.path = fname;
|
||||
|
||||
status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("(%s) fileinfo failed: %s\n",
|
||||
__location__, nt_errstr(status)));
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (finfo1.basic_info.out.create_time !=
|
||||
finfo2.basic_info.out.create_time) {
|
||||
torture_comment(tctx, "(%s) create_time changed\n", __location__);
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (finfo1.basic_info.out.access_time !=
|
||||
finfo2.basic_info.out.access_time) {
|
||||
torture_comment(tctx, "(%s) access_time changed\n", __location__);
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (finfo1.basic_info.out.write_time !=
|
||||
finfo2.basic_info.out.write_time) {
|
||||
torture_comment(tctx, "(%s) write_time changed\n", __location__);
|
||||
torture_comment(tctx, "write time conn 1 = %s, conn 2 = %s\n",
|
||||
nt_time_string(tctx, finfo1.basic_info.out.write_time),
|
||||
nt_time_string(tctx, finfo2.basic_info.out.write_time));
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (finfo1.basic_info.out.change_time !=
|
||||
finfo2.basic_info.out.change_time) {
|
||||
torture_comment(tctx, "(%s) change_time changed\n", __location__);
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* One of the two following calls updates the qpathinfo. */
|
||||
|
||||
/* If you had skipped the smbcli_write on fnum2, it would
|
||||
* *not* have updated the stat on disk */
|
||||
|
||||
smbcli_close(cli2->tree, fnum2);
|
||||
cli2 = NULL;
|
||||
|
||||
/* This call is only for the people looking at ethereal :-) */
|
||||
finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
|
||||
finfo2.basic_info.in.file.path = fname;
|
||||
|
||||
status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (fnum1 != -1)
|
||||
smbcli_close(cli->tree, fnum1);
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
smbcli_deltree(cli->tree, BASEDIR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
testing of delayed update of write_time
|
||||
*/
|
||||
struct torture_suite *torture_delay_write(void)
|
||||
{
|
||||
struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE");
|
||||
|
||||
torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
|
||||
torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
|
||||
torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
|
||||
|
||||
return suite;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
directory scanning tests
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/torture.h"
|
||||
#include "torture/util.h"
|
||||
#include "system/filesys.h"
|
||||
|
||||
static void list_fn(struct clilist_file_info *finfo, const char *name, void *state)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
test directory listing speed
|
||||
*/
|
||||
BOOL torture_dirtest1(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
int i;
|
||||
int fnum;
|
||||
BOOL correct = True;
|
||||
extern int torture_numops;
|
||||
struct timeval tv;
|
||||
|
||||
torture_comment(tctx, "Creating %d random filenames\n", torture_numops);
|
||||
|
||||
srandom(0);
|
||||
tv = timeval_current();
|
||||
for (i=0;i<torture_numops;i++) {
|
||||
char *fname;
|
||||
asprintf(&fname, "\\%x", (int)random());
|
||||
fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
|
||||
if (fnum == -1) {
|
||||
fprintf(stderr,"(%s) Failed to open %s\n",
|
||||
__location__, fname);
|
||||
return False;
|
||||
}
|
||||
smbcli_close(cli->tree, fnum);
|
||||
free(fname);
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Matched %d\n", smbcli_list(cli->tree, "a*.*", 0, list_fn, NULL));
|
||||
torture_comment(tctx, "Matched %d\n", smbcli_list(cli->tree, "b*.*", 0, list_fn, NULL));
|
||||
torture_comment(tctx, "Matched %d\n", smbcli_list(cli->tree, "xyzabc", 0, list_fn, NULL));
|
||||
|
||||
torture_comment(tctx, "dirtest core %g seconds\n", timeval_elapsed(&tv));
|
||||
|
||||
srandom(0);
|
||||
for (i=0;i<torture_numops;i++) {
|
||||
char *fname;
|
||||
asprintf(&fname, "\\%x", (int)random());
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
free(fname);
|
||||
}
|
||||
|
||||
return correct;
|
||||
}
|
||||
|
||||
BOOL torture_dirtest2(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
int i;
|
||||
int fnum, num_seen;
|
||||
BOOL correct = True;
|
||||
extern int torture_entries;
|
||||
|
||||
if (!torture_setup_dir(cli, "\\LISTDIR")) {
|
||||
return False;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Creating %d files\n", torture_entries);
|
||||
|
||||
/* Create torture_entries files and torture_entries directories. */
|
||||
for (i=0;i<torture_entries;i++) {
|
||||
char *fname;
|
||||
asprintf(&fname, "\\LISTDIR\\f%d", i);
|
||||
fnum = smbcli_nt_create_full(cli->tree, fname, 0,
|
||||
SEC_RIGHTS_FILE_ALL,
|
||||
FILE_ATTRIBUTE_ARCHIVE,
|
||||
NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE,
|
||||
NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
|
||||
if (fnum == -1) {
|
||||
fprintf(stderr,"(%s) Failed to open %s, error=%s\n",
|
||||
__location__, fname, smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
free(fname);
|
||||
smbcli_close(cli->tree, fnum);
|
||||
}
|
||||
for (i=0;i<torture_entries;i++) {
|
||||
char *fname;
|
||||
asprintf(&fname, "\\LISTDIR\\d%d", i);
|
||||
if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, fname))) {
|
||||
fprintf(stderr,"(%s) Failed to open %s, error=%s\n",
|
||||
__location__, fname, smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
free(fname);
|
||||
}
|
||||
|
||||
/* Now ensure that doing an old list sees both files and directories. */
|
||||
num_seen = smbcli_list_old(cli->tree, "\\LISTDIR\\*", FILE_ATTRIBUTE_DIRECTORY, list_fn, NULL);
|
||||
torture_comment(tctx, "num_seen = %d\n", num_seen );
|
||||
/* We should see (torture_entries) each of files & directories + . and .. */
|
||||
if (num_seen != (2*torture_entries)+2) {
|
||||
correct = False;
|
||||
fprintf(stderr,"(%s) entry count mismatch, should be %d, was %d\n",
|
||||
__location__, (2*torture_entries)+2, num_seen);
|
||||
}
|
||||
|
||||
|
||||
/* Ensure if we have the "must have" bits we only see the
|
||||
* relevant entries.
|
||||
*/
|
||||
num_seen = smbcli_list_old(cli->tree, "\\LISTDIR\\*", (FILE_ATTRIBUTE_DIRECTORY<<8)|FILE_ATTRIBUTE_DIRECTORY, list_fn, NULL);
|
||||
torture_comment(tctx, "num_seen = %d\n", num_seen );
|
||||
if (num_seen != torture_entries+2) {
|
||||
correct = False;
|
||||
fprintf(stderr,"(%s) entry count mismatch, should be %d, was %d\n",
|
||||
__location__, torture_entries+2, num_seen);
|
||||
}
|
||||
|
||||
num_seen = smbcli_list_old(cli->tree, "\\LISTDIR\\*", (FILE_ATTRIBUTE_ARCHIVE<<8)|FILE_ATTRIBUTE_DIRECTORY, list_fn, NULL);
|
||||
torture_comment(tctx, "num_seen = %d\n", num_seen );
|
||||
if (num_seen != torture_entries) {
|
||||
correct = False;
|
||||
fprintf(stderr,"(%s) entry count mismatch, should be %d, was %d\n",
|
||||
__location__, torture_entries, num_seen);
|
||||
}
|
||||
|
||||
/* Delete everything. */
|
||||
if (smbcli_deltree(cli->tree, "\\LISTDIR") == -1) {
|
||||
fprintf(stderr,"(%s) Failed to deltree %s, error=%s\n", "\\LISTDIR",
|
||||
__location__, smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
|
||||
#if 0
|
||||
torture_comment(tctx, "Matched %d\n", smbcli_list(cli->tree, "a*.*", 0, list_fn, NULL));
|
||||
torture_comment(tctx, "Matched %d\n", smbcli_list(cli->tree, "b*.*", 0, list_fn, NULL));
|
||||
torture_comment(tctx, "Matched %d\n", smbcli_list(cli->tree, "xyzabc", 0, list_fn, NULL));
|
||||
#endif
|
||||
|
||||
return correct;
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
test server handling of unexpected client disconnects
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "torture/torture.h"
|
||||
#include "system/filesys.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/util.h"
|
||||
|
||||
#define BASEDIR "\\test_disconnect"
|
||||
|
||||
#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)); \
|
||||
talloc_free(cli); \
|
||||
return False; \
|
||||
}} while (0)
|
||||
|
||||
/*
|
||||
test disconnect after async open
|
||||
*/
|
||||
static BOOL test_disconnect_open(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
union smb_open io;
|
||||
NTSTATUS status;
|
||||
struct smbcli_request *req1, *req2;
|
||||
|
||||
printf("trying open/disconnect\n");
|
||||
|
||||
io.generic.level = RAW_OPEN_NTCREATEX;
|
||||
io.ntcreatex.in.root_fid = 0;
|
||||
io.ntcreatex.in.flags = 0;
|
||||
io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA;
|
||||
io.ntcreatex.in.create_options = 0;
|
||||
io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
|
||||
io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ;
|
||||
io.ntcreatex.in.alloc_size = 0;
|
||||
io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
|
||||
io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
|
||||
io.ntcreatex.in.security_flags = 0;
|
||||
io.ntcreatex.in.fname = BASEDIR "\\open.dat";
|
||||
status = smb_raw_open(cli->tree, mem_ctx, &io);
|
||||
CHECK_STATUS(status, NT_STATUS_OK);
|
||||
|
||||
io.ntcreatex.in.share_access = 0;
|
||||
req1 = smb_raw_open_send(cli->tree, &io);
|
||||
req2 = smb_raw_open_send(cli->tree, &io);
|
||||
|
||||
status = smbcli_chkpath(cli->tree, "\\");
|
||||
CHECK_STATUS(status, NT_STATUS_OK);
|
||||
|
||||
talloc_free(cli);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
test disconnect with timed lock
|
||||
*/
|
||||
static BOOL test_disconnect_lock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
union smb_lock io;
|
||||
NTSTATUS status;
|
||||
int fnum;
|
||||
struct smbcli_request *req;
|
||||
struct smb_lock_entry lock[1];
|
||||
|
||||
printf("trying disconnect with async lock\n");
|
||||
|
||||
fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat",
|
||||
O_RDWR | O_CREAT, DENY_NONE);
|
||||
if (fnum == -1) {
|
||||
printf("open failed in mux_write - %s\n", smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
|
||||
io.lockx.level = RAW_LOCK_LOCKX;
|
||||
io.lockx.in.file.fnum = fnum;
|
||||
io.lockx.in.mode = 0;
|
||||
io.lockx.in.timeout = 0;
|
||||
io.lockx.in.lock_cnt = 1;
|
||||
io.lockx.in.ulock_cnt = 0;
|
||||
lock[0].pid = 1;
|
||||
lock[0].offset = 0;
|
||||
lock[0].count = 4;
|
||||
io.lockx.in.locks = &lock[0];
|
||||
|
||||
status = smb_raw_lock(cli->tree, &io);
|
||||
CHECK_STATUS(status, NT_STATUS_OK);
|
||||
|
||||
lock[0].pid = 2;
|
||||
io.lockx.in.timeout = 3000;
|
||||
req = smb_raw_lock_send(cli->tree, &io);
|
||||
|
||||
status = smbcli_chkpath(cli->tree, "\\");
|
||||
CHECK_STATUS(status, NT_STATUS_OK);
|
||||
|
||||
talloc_free(cli);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
basic testing of disconnects
|
||||
*/
|
||||
BOOL torture_disconnect(struct torture_context *torture)
|
||||
{
|
||||
BOOL ret = True;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
int i;
|
||||
extern int torture_numops;
|
||||
struct smbcli_state *cli;
|
||||
|
||||
mem_ctx = talloc_init("torture_raw_mux");
|
||||
|
||||
if (!torture_open_connection(&cli, 0)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (!torture_setup_dir(cli, BASEDIR)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
for (i=0;i<torture_numops;i++) {
|
||||
ret &= test_disconnect_lock(cli, mem_ctx);
|
||||
if (!torture_open_connection(&cli, 0)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
ret &= test_disconnect_open(cli, mem_ctx);
|
||||
if (!torture_open_connection(&cli, 0)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (torture_setting_bool(torture, "samba3", False)) {
|
||||
/*
|
||||
* In Samba3 it might happen that the old smbd from
|
||||
* test_disconnect_lock is not scheduled before the
|
||||
* new process comes in. Try to get rid of the random
|
||||
* failures in the build farm.
|
||||
*/
|
||||
msleep(200);
|
||||
}
|
||||
}
|
||||
|
||||
smb_raw_exit(cli->session);
|
||||
smbcli_deltree(cli->tree, BASEDIR);
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,812 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
basic locking tests
|
||||
|
||||
Copyright (C) Andrew Tridgell 2000-2004
|
||||
Copyright (C) Jeremy Allison 2000-2004
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/ui.h"
|
||||
#include "torture/util.h"
|
||||
#include "torture/torture.h"
|
||||
#include "system/time.h"
|
||||
#include "system/filesys.h"
|
||||
|
||||
#define BASEDIR "\\locktest"
|
||||
|
||||
/*
|
||||
This test checks for two things:
|
||||
|
||||
1) correct support for retaining locks over a close (ie. the server
|
||||
must not use posix semantics)
|
||||
2) support for lock timeouts
|
||||
*/
|
||||
bool torture_locktest1(struct torture_context *tctx,
|
||||
struct smbcli_state *cli1,
|
||||
struct smbcli_state *cli2)
|
||||
{
|
||||
const char *fname = BASEDIR "\\lockt1.lck";
|
||||
int fnum1, fnum2, fnum3;
|
||||
time_t t1, t2;
|
||||
uint_t lock_timeout;
|
||||
|
||||
if (!torture_setup_dir(cli1, BASEDIR)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
|
||||
torture_assert(tctx, fnum1 != -1,
|
||||
talloc_asprintf(tctx,
|
||||
"open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)));
|
||||
fnum2 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
|
||||
torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx,
|
||||
"open2 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)));
|
||||
fnum3 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
|
||||
torture_assert(tctx, fnum3 != -1, talloc_asprintf(tctx,
|
||||
"open3 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx,
|
||||
smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK),
|
||||
talloc_asprintf(tctx, "lock1 failed (%s)", smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)),
|
||||
"lock2 succeeded! This is a locking bug\n");
|
||||
|
||||
if (!check_error(__location__, cli2, ERRDOS, ERRlock,
|
||||
NT_STATUS_LOCK_NOT_GRANTED)) return False;
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)),
|
||||
"lock2 succeeded! This is a locking bug\n");
|
||||
|
||||
if (!check_error(__location__, cli2, ERRDOS, ERRlock,
|
||||
NT_STATUS_FILE_LOCK_CONFLICT)) return False;
|
||||
|
||||
torture_assert_ntstatus_ok(tctx,
|
||||
smbcli_lock(cli1->tree, fnum1, 5, 9, 0, WRITE_LOCK),
|
||||
talloc_asprintf(tctx,
|
||||
"lock1 failed (%s)", smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 5, 9, 0, WRITE_LOCK)),
|
||||
"lock2 succeeded! This is a locking bug");
|
||||
|
||||
if (!check_error(__location__, cli2, ERRDOS, ERRlock,
|
||||
NT_STATUS_LOCK_NOT_GRANTED)) return False;
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)),
|
||||
"lock2 succeeded! This is a locking bug");
|
||||
|
||||
if (!check_error(__location__, cli2, ERRDOS, ERRlock,
|
||||
NT_STATUS_LOCK_NOT_GRANTED)) return False;
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)),
|
||||
"lock2 succeeded! This is a locking bug");
|
||||
|
||||
if (!check_error(__location__, cli2, ERRDOS, ERRlock,
|
||||
NT_STATUS_FILE_LOCK_CONFLICT)) return False;
|
||||
|
||||
lock_timeout = (6 + (random() % 20));
|
||||
torture_comment(tctx, "Testing lock timeout with timeout=%u\n",
|
||||
lock_timeout);
|
||||
t1 = time(NULL);
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, lock_timeout * 1000, WRITE_LOCK)),
|
||||
"lock3 succeeded! This is a locking bug\n");
|
||||
|
||||
if (!check_error(__location__, cli2, ERRDOS, ERRlock,
|
||||
NT_STATUS_FILE_LOCK_CONFLICT)) return False;
|
||||
t2 = time(NULL);
|
||||
|
||||
if (t2 - t1 < 5) {
|
||||
torture_fail(tctx,
|
||||
"error: This server appears not to support timed lock requests");
|
||||
}
|
||||
torture_comment(tctx, "server slept for %u seconds for a %u second timeout\n",
|
||||
(uint_t)(t2-t1), lock_timeout);
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum2),
|
||||
talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)),
|
||||
"lock4 succeeded! This is a locking bug");
|
||||
|
||||
if (!check_error(__location__, cli2, ERRDOS, ERRlock,
|
||||
NT_STATUS_FILE_LOCK_CONFLICT)) return False;
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1),
|
||||
talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum3),
|
||||
talloc_asprintf(tctx, "close3 failed (%s)", smbcli_errstr(cli2->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname),
|
||||
talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
This test checks that
|
||||
|
||||
1) the server supports multiple locking contexts on the one SMB
|
||||
connection, distinguished by PID.
|
||||
|
||||
2) the server correctly fails overlapping locks made by the same PID (this
|
||||
goes against POSIX behaviour, which is why it is tricky to implement)
|
||||
|
||||
3) the server denies unlock requests by an incorrect client PID
|
||||
*/
|
||||
bool torture_locktest2(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
const char *fname = BASEDIR "\\lockt2.lck";
|
||||
int fnum1, fnum2, fnum3;
|
||||
|
||||
if (!torture_setup_dir(cli, BASEDIR)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Testing pid context\n");
|
||||
|
||||
cli->session->pid = 1;
|
||||
|
||||
fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
|
||||
torture_assert(tctx, fnum1 != -1,
|
||||
talloc_asprintf(tctx,
|
||||
"open of %s failed (%s)", fname, smbcli_errstr(cli->tree)));
|
||||
|
||||
fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
|
||||
torture_assert(tctx, fnum2 != -1,
|
||||
talloc_asprintf(tctx, "open2 of %s failed (%s)",
|
||||
fname, smbcli_errstr(cli->tree)));
|
||||
|
||||
cli->session->pid = 2;
|
||||
|
||||
fnum3 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
|
||||
torture_assert(tctx, fnum3 != -1,
|
||||
talloc_asprintf(tctx,
|
||||
"open3 of %s failed (%s)\n", fname, smbcli_errstr(cli->tree)));
|
||||
|
||||
cli->session->pid = 1;
|
||||
|
||||
torture_assert_ntstatus_ok(tctx,
|
||||
smbcli_lock(cli->tree, fnum1, 0, 4, 0, WRITE_LOCK),
|
||||
talloc_asprintf(tctx,
|
||||
"lock1 failed (%s)", smbcli_errstr(cli->tree)));
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum1, 0, 4, 0, WRITE_LOCK)),
|
||||
"WRITE lock1 succeeded! This is a locking bug");
|
||||
|
||||
if (!check_error(__location__, cli, ERRDOS, ERRlock,
|
||||
NT_STATUS_LOCK_NOT_GRANTED)) return False;
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum2, 0, 4, 0, WRITE_LOCK)),
|
||||
"WRITE lock2 succeeded! This is a locking bug");
|
||||
|
||||
if (!check_error(__location__, cli, ERRDOS, ERRlock,
|
||||
NT_STATUS_LOCK_NOT_GRANTED)) return False;
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum2, 0, 4, 0, READ_LOCK)),
|
||||
"READ lock2 succeeded! This is a locking bug");
|
||||
|
||||
if (!check_error(__location__, cli, ERRDOS, ERRlock,
|
||||
NT_STATUS_FILE_LOCK_CONFLICT)) return False;
|
||||
|
||||
torture_assert_ntstatus_ok(tctx,
|
||||
smbcli_lock(cli->tree, fnum1, 100, 4, 0, WRITE_LOCK),
|
||||
talloc_asprintf(tctx,
|
||||
"lock at 100 failed (%s)", smbcli_errstr(cli->tree)));
|
||||
|
||||
cli->session->pid = 2;
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_unlock(cli->tree, fnum1, 100, 4)),
|
||||
"unlock at 100 succeeded! This is a locking bug");
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_unlock(cli->tree, fnum1, 0, 4)),
|
||||
"unlock1 succeeded! This is a locking bug");
|
||||
|
||||
if (!check_error(__location__, cli,
|
||||
ERRDOS, ERRnotlocked,
|
||||
NT_STATUS_RANGE_NOT_LOCKED)) return False;
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_unlock(cli->tree, fnum1, 0, 8)),
|
||||
"unlock2 succeeded! This is a locking bug");
|
||||
|
||||
if (!check_error(__location__, cli,
|
||||
ERRDOS, ERRnotlocked,
|
||||
NT_STATUS_RANGE_NOT_LOCKED)) return False;
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum3, 0, 4, 0, WRITE_LOCK)),
|
||||
"lock3 succeeded! This is a locking bug");
|
||||
|
||||
if (!check_error(__location__, cli, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return False;
|
||||
|
||||
cli->session->pid = 1;
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum1),
|
||||
talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum2),
|
||||
talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum3),
|
||||
talloc_asprintf(tctx, "close3 failed (%s)", smbcli_errstr(cli->tree)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
This test checks that
|
||||
|
||||
1) the server supports the full offset range in lock requests
|
||||
*/
|
||||
bool torture_locktest3(struct torture_context *tctx,
|
||||
struct smbcli_state *cli1,
|
||||
struct smbcli_state *cli2)
|
||||
{
|
||||
const char *fname = BASEDIR "\\lockt3.lck";
|
||||
int fnum1, fnum2, i;
|
||||
uint32_t offset;
|
||||
extern int torture_numops;
|
||||
|
||||
#define NEXT_OFFSET offset += (~(uint32_t)0) / torture_numops
|
||||
|
||||
torture_comment(tctx, "Testing 32 bit offset ranges");
|
||||
|
||||
if (!torture_setup_dir(cli1, BASEDIR)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
|
||||
torture_assert(tctx, fnum1 != -1,
|
||||
talloc_asprintf(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)));
|
||||
fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
|
||||
torture_assert(tctx, fnum2 != -1,
|
||||
talloc_asprintf(tctx, "open2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)));
|
||||
|
||||
torture_comment(tctx, "Establishing %d locks\n", torture_numops);
|
||||
|
||||
for (offset=i=0;i<torture_numops;i++) {
|
||||
NEXT_OFFSET;
|
||||
torture_assert_ntstatus_ok(tctx,
|
||||
smbcli_lock(cli1->tree, fnum1, offset-1, 1, 0, WRITE_LOCK),
|
||||
talloc_asprintf(tctx, "lock1 %d failed (%s)", i, smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx,
|
||||
smbcli_lock(cli2->tree, fnum2, offset-2, 1, 0, WRITE_LOCK),
|
||||
talloc_asprintf(tctx, "lock2 %d failed (%s)",
|
||||
i, smbcli_errstr(cli1->tree)));
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Testing %d locks\n", torture_numops);
|
||||
|
||||
for (offset=i=0;i<torture_numops;i++) {
|
||||
NEXT_OFFSET;
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, offset-2, 1, 0, WRITE_LOCK)),
|
||||
talloc_asprintf(tctx, "error: lock1 %d succeeded!", i));
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, offset-1, 1, 0, WRITE_LOCK)),
|
||||
talloc_asprintf(tctx, "error: lock2 %d succeeded!", i));
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, offset-1, 1, 0, WRITE_LOCK)),
|
||||
talloc_asprintf(tctx, "error: lock3 %d succeeded!", i));
|
||||
|
||||
torture_assert(tctx,
|
||||
!NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, offset-2, 1, 0, WRITE_LOCK)),
|
||||
talloc_asprintf(tctx, "error: lock4 %d succeeded!", i));
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Removing %d locks\n", torture_numops);
|
||||
|
||||
for (offset=i=0;i<torture_numops;i++) {
|
||||
NEXT_OFFSET;
|
||||
|
||||
torture_assert_ntstatus_ok(tctx,
|
||||
smbcli_unlock(cli1->tree, fnum1, offset-1, 1),
|
||||
talloc_asprintf(tctx, "unlock1 %d failed (%s)",
|
||||
i,
|
||||
smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx,
|
||||
smbcli_unlock(cli2->tree, fnum2, offset-2, 1),
|
||||
talloc_asprintf(tctx, "unlock2 %d failed (%s)",
|
||||
i,
|
||||
smbcli_errstr(cli1->tree)));
|
||||
}
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1),
|
||||
talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum2),
|
||||
talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname),
|
||||
talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define EXPECTED(ret, v) if ((ret) != (v)) { \
|
||||
torture_comment(tctx, "** "); correct = False; \
|
||||
}
|
||||
|
||||
/*
|
||||
looks at overlapping locks
|
||||
*/
|
||||
BOOL torture_locktest4(struct torture_context *tctx,
|
||||
struct smbcli_state *cli1,
|
||||
struct smbcli_state *cli2)
|
||||
{
|
||||
const char *fname = BASEDIR "\\lockt4.lck";
|
||||
int fnum1, fnum2, f;
|
||||
BOOL ret;
|
||||
uint8_t buf[1000];
|
||||
BOOL correct = True;
|
||||
|
||||
if (!torture_setup_dir(cli1, BASEDIR)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
|
||||
fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
if (smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
|
||||
torture_comment(tctx, "Failed to create file\n");
|
||||
correct = False;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 2, 4, 0, WRITE_LOCK));
|
||||
EXPECTED(ret, False);
|
||||
torture_comment(tctx, "the same process %s set overlapping write locks\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 10, 4, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 12, 4, 0, READ_LOCK));
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "the same process %s set overlapping read locks\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 20, 4, 0, WRITE_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 22, 4, 0, WRITE_LOCK));
|
||||
EXPECTED(ret, False);
|
||||
torture_comment(tctx, "a different connection %s set overlapping write locks\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 30, 4, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 32, 4, 0, READ_LOCK));
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "a different connection %s set overlapping read locks\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 40, 4, 0, WRITE_LOCK))) &&
|
||||
NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 42, 4, 0, WRITE_LOCK)));
|
||||
EXPECTED(ret, False);
|
||||
torture_comment(tctx, "a different pid %s set overlapping write locks\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 50, 4, 0, READ_LOCK))) &&
|
||||
NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 52, 4, 0, READ_LOCK)));
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "a different pid %s set overlapping read locks\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 60, 4, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 60, 4, 0, READ_LOCK));
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "the same process %s set the same read lock twice\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 70, 4, 0, WRITE_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 70, 4, 0, WRITE_LOCK));
|
||||
EXPECTED(ret, False);
|
||||
torture_comment(tctx, "the same process %s set the same write lock twice\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 80, 4, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 80, 4, 0, WRITE_LOCK));
|
||||
EXPECTED(ret, False);
|
||||
torture_comment(tctx, "the same process %s overlay a read lock with a write lock\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 90, 4, 0, WRITE_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 90, 4, 0, READ_LOCK));
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "the same process %s overlay a write lock with a read lock\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 100, 4, 0, WRITE_LOCK))) &&
|
||||
NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 100, 4, 0, READ_LOCK)));
|
||||
EXPECTED(ret, False);
|
||||
torture_comment(tctx, "a different pid %s overlay a write lock with a read lock\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 110, 4, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 112, 4, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 110, 6));
|
||||
EXPECTED(ret, False);
|
||||
torture_comment(tctx, "the same process %s coalesce read locks\n", ret?"can":"cannot");
|
||||
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 120, 4, 0, WRITE_LOCK)) &&
|
||||
(smbcli_read(cli2->tree, fnum2, buf, 120, 4) == 4);
|
||||
EXPECTED(ret, False);
|
||||
torture_comment(tctx, "this server %s strict write locking\n", ret?"doesn't do":"does");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 130, 4, 0, READ_LOCK)) &&
|
||||
(smbcli_write(cli2->tree, fnum2, 0, buf, 130, 4) == 4);
|
||||
EXPECTED(ret, False);
|
||||
torture_comment(tctx, "this server %s strict read locking\n", ret?"doesn't do":"does");
|
||||
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 140, 4, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 140, 4, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 140, 4)) &&
|
||||
NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 140, 4));
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "this server %s do recursive read locking\n", ret?"does":"doesn't");
|
||||
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 150, 4, 0, WRITE_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 150, 4, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 150, 4)) &&
|
||||
(smbcli_read(cli2->tree, fnum2, buf, 150, 4) == 4) &&
|
||||
!(smbcli_write(cli2->tree, fnum2, 0, buf, 150, 4) == 4) &&
|
||||
NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 150, 4));
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "this server %s do recursive lock overlays\n", ret?"does":"doesn't");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 160, 4, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 160, 4)) &&
|
||||
(smbcli_write(cli2->tree, fnum2, 0, buf, 160, 4) == 4) &&
|
||||
(smbcli_read(cli2->tree, fnum2, buf, 160, 4) == 4);
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "the same process %s remove a read lock using write locking\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 170, 4, 0, WRITE_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 170, 4)) &&
|
||||
(smbcli_write(cli2->tree, fnum2, 0, buf, 170, 4) == 4) &&
|
||||
(smbcli_read(cli2->tree, fnum2, buf, 170, 4) == 4);
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "the same process %s remove a write lock using read locking\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 190, 4, 0, WRITE_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 190, 4, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 190, 4)) &&
|
||||
!(smbcli_write(cli2->tree, fnum2, 0, buf, 190, 4) == 4) &&
|
||||
(smbcli_read(cli2->tree, fnum2, buf, 190, 4) == 4);
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "the same process %s remove the first lock first\n", ret?"does":"doesn't");
|
||||
|
||||
smbcli_close(cli1->tree, fnum1);
|
||||
smbcli_close(cli2->tree, fnum2);
|
||||
fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
|
||||
f = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 8, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, f, 0, 1, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_close(cli1->tree, fnum1)) &&
|
||||
((fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE)) != -1) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 7, 1, 0, WRITE_LOCK));
|
||||
smbcli_close(cli1->tree, f);
|
||||
smbcli_close(cli1->tree, fnum1);
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "the server %s have the NT byte range lock bug\n", !ret?"does":"doesn't");
|
||||
|
||||
fail:
|
||||
smbcli_close(cli1->tree, fnum1);
|
||||
smbcli_close(cli2->tree, fnum2);
|
||||
smbcli_unlink(cli1->tree, fname);
|
||||
|
||||
return correct;
|
||||
}
|
||||
|
||||
/*
|
||||
looks at lock upgrade/downgrade.
|
||||
*/
|
||||
BOOL torture_locktest5(struct torture_context *tctx, struct smbcli_state *cli1,
|
||||
struct smbcli_state *cli2)
|
||||
{
|
||||
const char *fname = BASEDIR "\\lockt5.lck";
|
||||
int fnum1, fnum2, fnum3;
|
||||
BOOL ret;
|
||||
uint8_t buf[1000];
|
||||
BOOL correct = True;
|
||||
|
||||
if (!torture_setup_dir(cli1, BASEDIR)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
|
||||
fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
|
||||
fnum3 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) == sizeof(buf),
|
||||
"Failed to create file");
|
||||
|
||||
/* Check for NT bug... */
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 8, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum3, 0, 1, 0, READ_LOCK));
|
||||
smbcli_close(cli1->tree, fnum1);
|
||||
fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 7, 1, 0, WRITE_LOCK));
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "this server %s the NT locking bug\n", ret ? "doesn't have" : "has");
|
||||
smbcli_close(cli1->tree, fnum1);
|
||||
fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
|
||||
smbcli_unlock(cli1->tree, fnum3, 0, 1);
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 1, 1, 0, READ_LOCK));
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "the same process %s overlay a write with a read lock\n", ret?"can":"cannot");
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, READ_LOCK));
|
||||
EXPECTED(ret, False);
|
||||
|
||||
torture_comment(tctx, "a different processs %s get a read lock on the first process lock stack\n", ret?"can":"cannot");
|
||||
|
||||
/* Unlock the process 2 lock. */
|
||||
smbcli_unlock(cli2->tree, fnum2, 0, 4);
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum3, 0, 4, 0, READ_LOCK));
|
||||
EXPECTED(ret, False);
|
||||
|
||||
torture_comment(tctx, "the same processs on a different fnum %s get a read lock\n", ret?"can":"cannot");
|
||||
|
||||
/* Unlock the process 1 fnum3 lock. */
|
||||
smbcli_unlock(cli1->tree, fnum3, 0, 4);
|
||||
|
||||
/* Stack 2 more locks here. */
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, READ_LOCK)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, READ_LOCK));
|
||||
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "the same process %s stack read locks\n", ret?"can":"cannot");
|
||||
|
||||
/* Unlock the first process lock, then check this was the WRITE lock that was
|
||||
removed. */
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)) &&
|
||||
NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, READ_LOCK));
|
||||
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "the first unlock removes the %s lock\n", ret?"WRITE":"READ");
|
||||
|
||||
/* Unlock the process 2 lock. */
|
||||
smbcli_unlock(cli2->tree, fnum2, 0, 4);
|
||||
|
||||
/* We should have 3 stacked locks here. Ensure we need to do 3 unlocks. */
|
||||
|
||||
ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 1, 1)) &&
|
||||
NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)) &&
|
||||
NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4));
|
||||
|
||||
EXPECTED(ret, True);
|
||||
torture_comment(tctx, "the same process %s unlock the stack of 4 locks\n", ret?"can":"cannot");
|
||||
|
||||
/* Ensure the next unlock fails. */
|
||||
ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4));
|
||||
EXPECTED(ret, False);
|
||||
torture_comment(tctx, "the same process %s count the lock stack\n", !ret?"can":"cannot");
|
||||
|
||||
/* Ensure connection 2 can get a write lock. */
|
||||
ret = NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, WRITE_LOCK));
|
||||
EXPECTED(ret, True);
|
||||
|
||||
torture_comment(tctx, "a different processs %s get a write lock on the unlocked stack\n", ret?"can":"cannot");
|
||||
|
||||
|
||||
smbcli_close(cli1->tree, fnum1);
|
||||
smbcli_close(cli2->tree, fnum2);
|
||||
smbcli_unlink(cli1->tree, fname);
|
||||
|
||||
return correct;
|
||||
}
|
||||
|
||||
/*
|
||||
tries the unusual lockingX locktype bits
|
||||
*/
|
||||
BOOL torture_locktest6(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
const char *fname[1] = { "\\lock6.txt" };
|
||||
int i;
|
||||
int fnum;
|
||||
NTSTATUS status;
|
||||
|
||||
if (!torture_setup_dir(cli, BASEDIR)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
for (i=0;i<1;i++) {
|
||||
torture_comment(tctx, "Testing %s\n", fname[i]);
|
||||
|
||||
smbcli_unlink(cli->tree, fname[i]);
|
||||
|
||||
fnum = smbcli_open(cli->tree, fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
|
||||
status = smbcli_locktype(cli->tree, fnum, 0, 8, 0, LOCKING_ANDX_CHANGE_LOCKTYPE);
|
||||
smbcli_close(cli->tree, fnum);
|
||||
torture_comment(tctx, "CHANGE_LOCKTYPE gave %s\n", nt_errstr(status));
|
||||
|
||||
fnum = smbcli_open(cli->tree, fname[i], O_RDWR, DENY_NONE);
|
||||
status = smbcli_locktype(cli->tree, fnum, 0, 8, 0, LOCKING_ANDX_CANCEL_LOCK);
|
||||
smbcli_close(cli->tree, fnum);
|
||||
torture_comment(tctx, "CANCEL_LOCK gave %s\n", nt_errstr(status));
|
||||
|
||||
smbcli_unlink(cli->tree, fname[i]);
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
BOOL torture_locktest7(struct torture_context *tctx,
|
||||
struct smbcli_state *cli1)
|
||||
{
|
||||
const char *fname = BASEDIR "\\lockt7.lck";
|
||||
int fnum1;
|
||||
int fnum2 = -1;
|
||||
size_t size;
|
||||
uint8_t buf[200];
|
||||
BOOL correct = False;
|
||||
|
||||
torture_assert(tctx, torture_setup_dir(cli1, BASEDIR),
|
||||
talloc_asprintf(tctx, "Unable to set up %s", BASEDIR));
|
||||
|
||||
fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) == sizeof(buf),
|
||||
"Failed to create file");
|
||||
|
||||
cli1->session->pid = 1;
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_lock(cli1->tree, fnum1, 130, 4, 0, READ_LOCK),
|
||||
talloc_asprintf(tctx, "Unable to apply read lock on range 130:4, error was %s",
|
||||
smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_comment(tctx, "pid1 successfully locked range 130:4 for READ\n");
|
||||
|
||||
torture_assert(tctx, smbcli_read(cli1->tree, fnum1, buf, 130, 4) == 4,
|
||||
talloc_asprintf(tctx, "pid1 unable to read the range 130:4, error was %s)",
|
||||
smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_comment(tctx, "pid1 successfully read the range 130:4\n");
|
||||
|
||||
if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) {
|
||||
torture_comment(tctx, "pid1 unable to write to the range 130:4, error was %s\n", smbcli_errstr(cli1->tree));
|
||||
torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT,
|
||||
"Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)");
|
||||
} else {
|
||||
torture_fail(tctx, "pid1 successfully wrote to the range 130:4 (should be denied)");
|
||||
}
|
||||
|
||||
cli1->session->pid = 2;
|
||||
|
||||
if (smbcli_read(cli1->tree, fnum1, buf, 130, 4) != 4) {
|
||||
torture_comment(tctx, "pid2 unable to read the range 130:4, error was %s\n", smbcli_errstr(cli1->tree));
|
||||
} else {
|
||||
torture_comment(tctx, "pid2 successfully read the range 130:4\n");
|
||||
}
|
||||
|
||||
if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) {
|
||||
torture_comment(tctx, "pid2 unable to write to the range 130:4, error was %s\n", smbcli_errstr(cli1->tree));
|
||||
torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT,
|
||||
"Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)");
|
||||
} else {
|
||||
torture_fail(tctx, "pid2 successfully wrote to the range 130:4 (should be denied)");
|
||||
}
|
||||
|
||||
cli1->session->pid = 1;
|
||||
smbcli_unlock(cli1->tree, fnum1, 130, 4);
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_lock(cli1->tree, fnum1, 130, 4, 0, WRITE_LOCK),
|
||||
talloc_asprintf(tctx, "Unable to apply write lock on range 130:4, error was %s",
|
||||
smbcli_errstr(cli1->tree)));
|
||||
torture_comment(tctx, "pid1 successfully locked range 130:4 for WRITE\n");
|
||||
|
||||
torture_assert(tctx, smbcli_read(cli1->tree, fnum1, buf, 130, 4) == 4,
|
||||
talloc_asprintf(tctx, "pid1 unable to read the range 130:4, error was %s",
|
||||
smbcli_errstr(cli1->tree)));
|
||||
torture_comment(tctx, "pid1 successfully read the range 130:4\n");
|
||||
|
||||
torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) == 4,
|
||||
talloc_asprintf(tctx, "pid1 unable to write to the range 130:4, error was %s",
|
||||
smbcli_errstr(cli1->tree)));
|
||||
torture_comment(tctx, "pid1 successfully wrote to the range 130:4\n");
|
||||
|
||||
cli1->session->pid = 2;
|
||||
|
||||
if (smbcli_read(cli1->tree, fnum1, buf, 130, 4) != 4) {
|
||||
torture_comment(tctx, "pid2 unable to read the range 130:4, error was %s\n",
|
||||
smbcli_errstr(cli1->tree));
|
||||
torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT,
|
||||
"Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)");
|
||||
} else {
|
||||
torture_fail(tctx, "pid2 successfully read the range 130:4 (should be denied)");
|
||||
}
|
||||
|
||||
if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) {
|
||||
torture_comment(tctx, "pid2 unable to write to the range 130:4, error was %s\n",
|
||||
smbcli_errstr(cli1->tree));
|
||||
if (!NT_STATUS_EQUAL(smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT)) {
|
||||
torture_comment(tctx, "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT) (%s)\n",
|
||||
__location__);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
torture_comment(tctx, "pid2 successfully wrote to the range 130:4 (should be denied) (%s)\n",
|
||||
__location__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Testing truncate of locked file.\n");
|
||||
|
||||
fnum2 = smbcli_open(cli1->tree, fname, O_RDWR|O_TRUNC, DENY_NONE);
|
||||
|
||||
torture_assert(tctx, fnum2 != -1, "Unable to truncate locked file");
|
||||
|
||||
torture_comment(tctx, "Truncated locked file.\n");
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_getatr(cli1->tree, fname, NULL, &size, NULL),
|
||||
talloc_asprintf(tctx, "getatr failed (%s)", smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert(tctx, size == 0, talloc_asprintf(tctx, "Unable to truncate locked file. Size was %u", (unsigned)size));
|
||||
|
||||
cli1->session->pid = 1;
|
||||
|
||||
smbcli_unlock(cli1->tree, fnum1, 130, 4);
|
||||
correct = True;
|
||||
|
||||
fail:
|
||||
smbcli_close(cli1->tree, fnum1);
|
||||
smbcli_close(cli1->tree, fnum2);
|
||||
smbcli_unlink(cli1->tree, fname);
|
||||
|
||||
return correct;
|
||||
}
|
||||
|
||||
struct torture_suite *torture_base_locktest(void)
|
||||
{
|
||||
struct torture_suite *suite = torture_suite_create(talloc_autofree_context(),
|
||||
"LOCK");
|
||||
torture_suite_add_2smb_test(suite, "LOCK1", torture_locktest1);
|
||||
torture_suite_add_1smb_test(suite, "LOCK2", torture_locktest2);
|
||||
torture_suite_add_2smb_test(suite, "LOCK3", torture_locktest3);
|
||||
torture_suite_add_2smb_test(suite, "LOCK4", torture_locktest4);
|
||||
torture_suite_add_2smb_test(suite, "LOCK5", torture_locktest5);
|
||||
torture_suite_add_1smb_test(suite, "LOCK6", torture_locktest6);
|
||||
torture_suite_add_1smb_test(suite, "LOCK7", torture_locktest7);
|
||||
|
||||
return suite;
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMB torture tester - mangling test
|
||||
Copyright (C) Andrew Tridgell 2002
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "torture/torture.h"
|
||||
#include "system/filesys.h"
|
||||
#include "system/dir.h"
|
||||
#include "lib/tdb/include/tdb.h"
|
||||
#include "lib/util/util_tdb.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/util.h"
|
||||
#include "pstring.h"
|
||||
|
||||
static TDB_CONTEXT *tdb;
|
||||
|
||||
#define NAME_LENGTH 20
|
||||
|
||||
static uint_t total, collisions, failures;
|
||||
|
||||
static BOOL test_one(struct smbcli_state *cli, const char *name)
|
||||
{
|
||||
int fnum;
|
||||
const char *shortname;
|
||||
fstring name2;
|
||||
NTSTATUS status;
|
||||
TDB_DATA data;
|
||||
|
||||
total++;
|
||||
|
||||
fnum = smbcli_open(cli->tree, name, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
|
||||
if (fnum == -1) {
|
||||
printf("open of %s failed (%s)\n", name, smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnum))) {
|
||||
printf("close of %s failed (%s)\n", name, smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
|
||||
/* get the short name */
|
||||
status = smbcli_qpathinfo_alt_name(cli->tree, name, &shortname);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
printf("query altname of %s failed (%s)\n", name, smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
|
||||
snprintf(name2, sizeof(name2), "\\mangle_test\\%s", shortname);
|
||||
if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, name2))) {
|
||||
printf("unlink of %s (%s) failed (%s)\n",
|
||||
name2, name, smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
|
||||
/* recreate by short name */
|
||||
fnum = smbcli_open(cli->tree, name2, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
|
||||
if (fnum == -1) {
|
||||
printf("open2 of %s failed (%s)\n", name2, smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnum))) {
|
||||
printf("close of %s failed (%s)\n", name, smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
|
||||
/* and unlink by long name */
|
||||
if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, name))) {
|
||||
printf("unlink2 of %s (%s) failed (%s)\n",
|
||||
name, name2, smbcli_errstr(cli->tree));
|
||||
failures++;
|
||||
smbcli_unlink(cli->tree, name2);
|
||||
return True;
|
||||
}
|
||||
|
||||
/* see if the short name is already in the tdb */
|
||||
data = tdb_fetch_bystring(tdb, shortname);
|
||||
if (data.dptr) {
|
||||
/* maybe its a duplicate long name? */
|
||||
if (strcasecmp(name, (const char *)data.dptr) != 0) {
|
||||
/* we have a collision */
|
||||
collisions++;
|
||||
printf("Collision between %s and %s -> %s "
|
||||
" (coll/tot: %u/%u)\n",
|
||||
name, data.dptr, shortname, collisions, total);
|
||||
}
|
||||
free(data.dptr);
|
||||
} else {
|
||||
TDB_DATA namedata;
|
||||
/* store it for later */
|
||||
namedata.dptr = discard_const_p(uint8_t, name);
|
||||
namedata.dsize = strlen(name)+1;
|
||||
tdb_store_bystring(tdb, shortname, namedata, TDB_REPLACE);
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
static void gen_name(char *name)
|
||||
{
|
||||
const char *chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-$~...";
|
||||
uint_t max_idx = strlen(chars);
|
||||
uint_t len;
|
||||
int i;
|
||||
char *p;
|
||||
|
||||
fstrcpy(name, "\\mangle_test\\");
|
||||
p = name + strlen(name);
|
||||
|
||||
len = 1 + random() % NAME_LENGTH;
|
||||
|
||||
for (i=0;i<len;i++) {
|
||||
p[i] = chars[random() % max_idx];
|
||||
}
|
||||
|
||||
p[i] = 0;
|
||||
|
||||
if (ISDOT(p) || ISDOTDOT(p)) {
|
||||
p[0] = '_';
|
||||
}
|
||||
|
||||
/* have a high probability of a common lead char */
|
||||
if (random() % 2 == 0) {
|
||||
p[0] = 'A';
|
||||
}
|
||||
|
||||
/* and a medium probability of a common lead string */
|
||||
if (random() % 10 == 0) {
|
||||
strncpy(p, "ABCDE", 5);
|
||||
}
|
||||
|
||||
/* and a high probability of a good extension length */
|
||||
if (random() % 2 == 0) {
|
||||
char *s = strrchr(p, '.');
|
||||
if (s) {
|
||||
s[4] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOL torture_mangle(struct torture_context *torture,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
extern int torture_numops;
|
||||
int i;
|
||||
|
||||
/* we will use an internal tdb to store the names we have used */
|
||||
tdb = tdb_open(NULL, 100000, TDB_INTERNAL, 0, 0);
|
||||
if (!tdb) {
|
||||
printf("ERROR: Failed to open tdb\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
if (!torture_setup_dir(cli, "\\mangle_test")) {
|
||||
return False;
|
||||
}
|
||||
|
||||
for (i=0;i<torture_numops;i++) {
|
||||
fstring name;
|
||||
|
||||
ZERO_STRUCT(name);
|
||||
|
||||
gen_name(name);
|
||||
|
||||
if (!test_one(cli, name)) {
|
||||
break;
|
||||
}
|
||||
if (total && total % 100 == 0) {
|
||||
printf("collisions %u/%u - %.2f%% (%u failures)\r",
|
||||
collisions, total, (100.0*collisions) / total, failures);
|
||||
}
|
||||
}
|
||||
|
||||
smbcli_unlink(cli->tree, "\\mangle_test\\*");
|
||||
if (NT_STATUS_IS_ERR(smbcli_rmdir(cli->tree, "\\mangle_test"))) {
|
||||
printf("ERROR: Failed to remove directory\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
printf("\nTotal collisions %u/%u - %.2f%% (%u failures)\n",
|
||||
collisions, total, (100.0*collisions) / total, failures);
|
||||
|
||||
return (failures == 0);
|
||||
}
|
||||
@@ -0,0 +1,857 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMB torture tester
|
||||
Copyright (C) Andrew Tridgell 1997-2003
|
||||
Copyright (C) Jelmer Vernooij 2006
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "system/time.h"
|
||||
#include "system/wait.h"
|
||||
#include "system/filesys.h"
|
||||
#include "libcli/raw/ioctl.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "libcli/resolve/resolve.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "librpc/gen_ndr/ndr_nbt.h"
|
||||
#include "torture/torture.h"
|
||||
#include "torture/util.h"
|
||||
#include "libcli/smb_composite/smb_composite.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
|
||||
extern struct cli_credentials *cmdline_credentials;
|
||||
static void benchrw_callback(struct smbcli_request *req);
|
||||
enum benchrw_stage {
|
||||
START,
|
||||
OPEN_CONNECTION,
|
||||
CLEANUP_TESTDIR,
|
||||
MK_TESTDIR,
|
||||
OPEN_FILE,
|
||||
INITIAL_WRITE,
|
||||
READ_WRITE_DATA,
|
||||
MAX_OPS_REACHED,
|
||||
ERROR,
|
||||
CLOSE_FILE,
|
||||
CLEANUP,
|
||||
FINISHED
|
||||
};
|
||||
|
||||
struct benchrw_state{
|
||||
struct torture_context *tctx;
|
||||
char *dname;
|
||||
char *fname;
|
||||
uint16_t fnum;
|
||||
int nr;
|
||||
struct smbcli_tree *cli;
|
||||
uint8_t *buffer;
|
||||
int writecnt;
|
||||
int readcnt;
|
||||
int completed;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
void *req_params;
|
||||
enum benchrw_stage mode;
|
||||
struct params{
|
||||
struct unclist{
|
||||
const char *host;
|
||||
const char *share;
|
||||
} **unc;
|
||||
const char *workgroup;
|
||||
int retry;
|
||||
unsigned int writeblocks;
|
||||
unsigned int blocksize;
|
||||
unsigned int writeratio;
|
||||
} *lp_params;
|
||||
};
|
||||
|
||||
static BOOL wait_lock(struct smbcli_state *c, int fnum, uint32_t offset, uint32_t len)
|
||||
{
|
||||
while (NT_STATUS_IS_ERR(smbcli_lock(c->tree, fnum, offset, len, -1, WRITE_LOCK))) {
|
||||
if (!check_error(__location__, c, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return False;
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
static BOOL rw_torture(struct torture_context *tctx, struct smbcli_state *c)
|
||||
{
|
||||
const char *lockfname = "\\torture.lck";
|
||||
char *fname;
|
||||
int fnum;
|
||||
int fnum2;
|
||||
pid_t pid2, pid = getpid();
|
||||
int i, j;
|
||||
uint8_t buf[1024];
|
||||
BOOL correct = True;
|
||||
|
||||
fnum2 = smbcli_open(c->tree, lockfname, O_RDWR | O_CREAT | O_EXCL,
|
||||
DENY_NONE);
|
||||
if (fnum2 == -1)
|
||||
fnum2 = smbcli_open(c->tree, lockfname, O_RDWR, DENY_NONE);
|
||||
if (fnum2 == -1) {
|
||||
torture_comment(tctx, "open of %s failed (%s)\n", lockfname, smbcli_errstr(c->tree));
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
for (i=0;i<torture_numops;i++) {
|
||||
uint_t n = (uint_t)random()%10;
|
||||
if (i % 10 == 0) {
|
||||
torture_comment(tctx, "%d\r", i); fflush(stdout);
|
||||
}
|
||||
asprintf(&fname, "\\torture.%u", n);
|
||||
|
||||
if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) {
|
||||
return False;
|
||||
}
|
||||
|
||||
fnum = smbcli_open(c->tree, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_ALL);
|
||||
if (fnum == -1) {
|
||||
torture_comment(tctx, "open failed (%s)\n", smbcli_errstr(c->tree));
|
||||
correct = False;
|
||||
break;
|
||||
}
|
||||
|
||||
if (smbcli_write(c->tree, fnum, 0, &pid, 0, sizeof(pid)) != sizeof(pid)) {
|
||||
torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree));
|
||||
correct = False;
|
||||
}
|
||||
|
||||
for (j=0;j<50;j++) {
|
||||
if (smbcli_write(c->tree, fnum, 0, buf,
|
||||
sizeof(pid)+(j*sizeof(buf)),
|
||||
sizeof(buf)) != sizeof(buf)) {
|
||||
torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree));
|
||||
correct = False;
|
||||
}
|
||||
}
|
||||
|
||||
pid2 = 0;
|
||||
|
||||
if (smbcli_read(c->tree, fnum, &pid2, 0, sizeof(pid)) != sizeof(pid)) {
|
||||
torture_comment(tctx, "read failed (%s)\n", smbcli_errstr(c->tree));
|
||||
correct = False;
|
||||
}
|
||||
|
||||
if (pid2 != pid) {
|
||||
torture_comment(tctx, "data corruption!\n");
|
||||
correct = False;
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_ERR(smbcli_close(c->tree, fnum))) {
|
||||
torture_comment(tctx, "close failed (%s)\n", smbcli_errstr(c->tree));
|
||||
correct = False;
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_ERR(smbcli_unlink(c->tree, fname))) {
|
||||
torture_comment(tctx, "unlink failed (%s)\n", smbcli_errstr(c->tree));
|
||||
correct = False;
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_ERR(smbcli_unlock(c->tree, fnum2, n*sizeof(int), sizeof(int)))) {
|
||||
torture_comment(tctx, "unlock failed (%s)\n", smbcli_errstr(c->tree));
|
||||
correct = False;
|
||||
}
|
||||
free(fname);
|
||||
}
|
||||
|
||||
smbcli_close(c->tree, fnum2);
|
||||
smbcli_unlink(c->tree, lockfname);
|
||||
|
||||
torture_comment(tctx, "%d\n", i);
|
||||
|
||||
return correct;
|
||||
}
|
||||
|
||||
BOOL run_torture(struct torture_context *tctx, struct smbcli_state *cli, int dummy)
|
||||
{
|
||||
return rw_torture(tctx, cli);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
see how many RPC pipes we can open at once
|
||||
*/
|
||||
BOOL run_pipe_number(struct torture_context *tctx,
|
||||
struct smbcli_state *cli1)
|
||||
{
|
||||
const char *pipe_name = "\\WKSSVC";
|
||||
int fnum;
|
||||
int num_pipes = 0;
|
||||
|
||||
while(1) {
|
||||
fnum = smbcli_nt_create_full(cli1->tree, pipe_name, 0, SEC_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
|
||||
NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, NTCREATEX_DISP_OPEN_IF, 0, 0);
|
||||
|
||||
if (fnum == -1) {
|
||||
torture_comment(tctx, "Open of pipe %s failed with error (%s)\n", pipe_name, smbcli_errstr(cli1->tree));
|
||||
break;
|
||||
}
|
||||
num_pipes++;
|
||||
torture_comment(tctx, "%d\r", num_pipes);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
torture_comment(tctx, "pipe_number test - we can open %d %s pipes.\n", num_pipes, pipe_name );
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
open N connections to the server and just hold them open
|
||||
used for testing performance when there are N idle users
|
||||
already connected
|
||||
*/
|
||||
BOOL torture_holdcon(struct torture_context *tctx)
|
||||
{
|
||||
int i;
|
||||
struct smbcli_state **cli;
|
||||
int num_dead = 0;
|
||||
|
||||
torture_comment(tctx, "Opening %d connections\n", torture_numops);
|
||||
|
||||
cli = malloc_array_p(struct smbcli_state *, torture_numops);
|
||||
|
||||
for (i=0;i<torture_numops;i++) {
|
||||
if (!torture_open_connection(&cli[i], i)) {
|
||||
return False;
|
||||
}
|
||||
torture_comment(tctx, "opened %d connections\r", i);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
torture_comment(tctx, "\nStarting pings\n");
|
||||
|
||||
while (1) {
|
||||
for (i=0;i<torture_numops;i++) {
|
||||
NTSTATUS status;
|
||||
if (cli[i]) {
|
||||
status = smbcli_chkpath(cli[i]->tree, "\\");
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
torture_comment(tctx, "Connection %d is dead\n", i);
|
||||
cli[i] = NULL;
|
||||
num_dead++;
|
||||
}
|
||||
usleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
if (num_dead == torture_numops) {
|
||||
torture_comment(tctx, "All connections dead - finishing\n");
|
||||
break;
|
||||
}
|
||||
|
||||
torture_comment(tctx, ".");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
test how many open files this server supports on the one socket
|
||||
*/
|
||||
BOOL run_maxfidtest(struct torture_context *tctx, struct smbcli_state *cli, int dummy)
|
||||
{
|
||||
#define MAXFID_TEMPLATE "\\maxfid\\fid%d\\maxfid.%d.%d"
|
||||
char *fname;
|
||||
int fnums[0x11000], i;
|
||||
int retries=4, maxfid;
|
||||
BOOL correct = True;
|
||||
|
||||
if (retries <= 0) {
|
||||
torture_comment(tctx, "failed to connect\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
if (smbcli_deltree(cli->tree, "\\maxfid") == -1) {
|
||||
torture_comment(tctx, "Failed to deltree \\maxfid - %s\n",
|
||||
smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, "\\maxfid"))) {
|
||||
torture_comment(tctx, "Failed to mkdir \\maxfid, error=%s\n",
|
||||
smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Testing maximum number of open files\n");
|
||||
|
||||
for (i=0; i<0x11000; i++) {
|
||||
if (i % 1000 == 0) {
|
||||
asprintf(&fname, "\\maxfid\\fid%d", i/1000);
|
||||
if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, fname))) {
|
||||
torture_comment(tctx, "Failed to mkdir %s, error=%s\n",
|
||||
fname, smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
free(fname);
|
||||
}
|
||||
asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid());
|
||||
if ((fnums[i] = smbcli_open(cli->tree, fname,
|
||||
O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) ==
|
||||
-1) {
|
||||
torture_comment(tctx, "open of %s failed (%s)\n",
|
||||
fname, smbcli_errstr(cli->tree));
|
||||
torture_comment(tctx, "maximum fnum is %d\n", i);
|
||||
break;
|
||||
}
|
||||
free(fname);
|
||||
torture_comment(tctx, "%6d\r", i);
|
||||
}
|
||||
torture_comment(tctx, "%6d\n", i);
|
||||
i--;
|
||||
|
||||
maxfid = i;
|
||||
|
||||
torture_comment(tctx, "cleaning up\n");
|
||||
for (i=0;i<maxfid/2;i++) {
|
||||
asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid());
|
||||
if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnums[i]))) {
|
||||
torture_comment(tctx, "Close of fnum %d failed - %s\n", fnums[i], smbcli_errstr(cli->tree));
|
||||
}
|
||||
if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fname))) {
|
||||
torture_comment(tctx, "unlink of %s failed (%s)\n",
|
||||
fname, smbcli_errstr(cli->tree));
|
||||
correct = False;
|
||||
}
|
||||
free(fname);
|
||||
|
||||
asprintf(&fname, MAXFID_TEMPLATE, (maxfid-i)/1000, maxfid-i,(int)getpid());
|
||||
if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnums[maxfid-i]))) {
|
||||
torture_comment(tctx, "Close of fnum %d failed - %s\n", fnums[maxfid-i], smbcli_errstr(cli->tree));
|
||||
}
|
||||
if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fname))) {
|
||||
torture_comment(tctx, "unlink of %s failed (%s)\n",
|
||||
fname, smbcli_errstr(cli->tree));
|
||||
correct = False;
|
||||
}
|
||||
free(fname);
|
||||
|
||||
torture_comment(tctx, "%6d %6d\r", i, maxfid-i);
|
||||
}
|
||||
torture_comment(tctx, "%6d\n", 0);
|
||||
|
||||
if (smbcli_deltree(cli->tree, "\\maxfid") == -1) {
|
||||
torture_comment(tctx, "Failed to deltree \\maxfid - %s\n",
|
||||
smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "maxfid test finished\n");
|
||||
if (!torture_close_connection(cli)) {
|
||||
correct = False;
|
||||
}
|
||||
return correct;
|
||||
#undef MAXFID_TEMPLATE
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
sees what IOCTLs are supported
|
||||
*/
|
||||
BOOL torture_ioctl_test(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
uint16_t device, function;
|
||||
int fnum;
|
||||
const char *fname = "\\ioctl.dat";
|
||||
NTSTATUS status;
|
||||
union smb_ioctl parms;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
mem_ctx = talloc_named_const(tctx, 0, "ioctl_test");
|
||||
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
|
||||
fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
|
||||
if (fnum == -1) {
|
||||
torture_comment(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
|
||||
return False;
|
||||
}
|
||||
|
||||
parms.ioctl.level = RAW_IOCTL_IOCTL;
|
||||
parms.ioctl.in.file.fnum = fnum;
|
||||
parms.ioctl.in.request = IOCTL_QUERY_JOB_INFO;
|
||||
status = smb_raw_ioctl(cli->tree, mem_ctx, &parms);
|
||||
torture_comment(tctx, "ioctl job info: %s\n", smbcli_errstr(cli->tree));
|
||||
|
||||
for (device=0;device<0x100;device++) {
|
||||
torture_comment(tctx, "testing device=0x%x\n", device);
|
||||
for (function=0;function<0x100;function++) {
|
||||
parms.ioctl.in.request = (device << 16) | function;
|
||||
status = smb_raw_ioctl(cli->tree, mem_ctx, &parms);
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
torture_comment(tctx, "ioctl device=0x%x function=0x%x OK : %d bytes\n",
|
||||
device, function, (int)parms.ioctl.out.blob.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
init params using lp_parm_xxx
|
||||
return number of unclist entries
|
||||
*/
|
||||
static int init_benchrw_params(struct torture_context *tctx, struct params *lpar)
|
||||
{
|
||||
char **unc_list = NULL;
|
||||
int num_unc_names = 0, conn_index=0, empty_lines=0;
|
||||
const char *p;
|
||||
lpar->retry = torture_setting_int(tctx, "retry",3);
|
||||
lpar->blocksize = torture_setting_int(tctx, "blocksize",65535);
|
||||
lpar->writeblocks = torture_setting_int(tctx, "writeblocks",15);
|
||||
lpar->writeratio = torture_setting_int(tctx, "writeratio",5);
|
||||
lpar->workgroup = lp_workgroup();
|
||||
|
||||
p = torture_setting_string(tctx, "unclist", NULL);
|
||||
if (p) {
|
||||
char *h, *s;
|
||||
unc_list = file_lines_load(p, &num_unc_names, NULL);
|
||||
if (!unc_list || num_unc_names <= 0) {
|
||||
torture_comment(tctx, "Failed to load unc names list from '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
lpar->unc = talloc_array(tctx, struct unclist *, (num_unc_names-empty_lines));
|
||||
for(conn_index = 0; conn_index < num_unc_names; conn_index++) {
|
||||
/* ignore empty lines */
|
||||
if(strlen(unc_list[conn_index % num_unc_names])==0){
|
||||
empty_lines++;
|
||||
continue;
|
||||
}
|
||||
if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
|
||||
NULL, &h, &s)) {
|
||||
torture_comment(tctx, "Failed to parse UNC name %s\n",
|
||||
unc_list[conn_index % num_unc_names]);
|
||||
exit(1);
|
||||
}
|
||||
lpar->unc[conn_index-empty_lines] = talloc(tctx,struct unclist);
|
||||
lpar->unc[conn_index-empty_lines]->host = h;
|
||||
lpar->unc[conn_index-empty_lines]->share = s;
|
||||
}
|
||||
return num_unc_names-empty_lines;
|
||||
}else{
|
||||
lpar->unc = talloc_array(tctx, struct unclist *, 1);
|
||||
lpar->unc[0] = talloc(tctx,struct unclist);
|
||||
lpar->unc[0]->host = torture_setting_string(tctx, "host", NULL);
|
||||
lpar->unc[0]->share = torture_setting_string(tctx, "share", NULL);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Called when the reads & writes are finished. closes the file.
|
||||
*/
|
||||
static NTSTATUS benchrw_close(struct torture_context *tctx,struct smbcli_request *req,
|
||||
struct benchrw_state *state)
|
||||
{
|
||||
union smb_close close_parms;
|
||||
|
||||
NT_STATUS_NOT_OK_RETURN(req->status);
|
||||
|
||||
torture_comment(tctx, "Close file %d (%d)\n",state->nr,state->fnum);
|
||||
close_parms.close.level = RAW_CLOSE_CLOSE;
|
||||
close_parms.close.in.file.fnum = state->fnum ;
|
||||
close_parms.close.in.write_time = 0;
|
||||
state->mode=CLOSE_FILE;
|
||||
|
||||
req = smb_raw_close_send(state->cli, &close_parms);
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
/*register the callback function!*/
|
||||
req->async.fn = benchrw_callback;
|
||||
req->async.private = state;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
Called when the initial write is completed is done. write or read a file.
|
||||
*/
|
||||
static NTSTATUS benchrw_readwrite(struct torture_context *tctx,struct smbcli_request *req,
|
||||
struct benchrw_state *state)
|
||||
{
|
||||
union smb_read rd;
|
||||
union smb_write wr;
|
||||
|
||||
NT_STATUS_NOT_OK_RETURN(req->status);
|
||||
|
||||
state->completed++;
|
||||
/*rotate between writes and reads*/
|
||||
if( state->completed % state->lp_params->writeratio == 0){
|
||||
torture_comment(tctx, "Callback WRITE file:%d (%d/%d)\n",
|
||||
state->nr,state->completed,torture_numops);
|
||||
wr.generic.level = RAW_WRITE_WRITEX ;
|
||||
wr.writex.in.file.fnum = state->fnum ;
|
||||
wr.writex.in.offset = 0;
|
||||
wr.writex.in.wmode = 0 ;
|
||||
wr.writex.in.remaining = 0;
|
||||
wr.writex.in.count = state->lp_params->blocksize;
|
||||
wr.writex.in.data = state->buffer;
|
||||
state->readcnt=0;
|
||||
req = smb_raw_write_send(state->cli,&wr);
|
||||
}else{
|
||||
torture_comment(tctx, "Callback READ file:%d (%d/%d) Offset:%d\n",
|
||||
state->nr,state->completed,torture_numops,
|
||||
(state->readcnt*state->lp_params->blocksize));
|
||||
rd.generic.level = RAW_READ_READ ;
|
||||
rd.read.in.file.fnum = state->fnum ;
|
||||
rd.read.in.offset = state->readcnt *
|
||||
state->lp_params->blocksize;
|
||||
rd.read.in.count = state->lp_params->blocksize;
|
||||
rd.read.in.remaining = 0 ;
|
||||
rd.read.out.data = state->buffer;
|
||||
if(state->readcnt < state->lp_params->writeblocks){
|
||||
state->readcnt++;
|
||||
}else{
|
||||
/*start reading from beginn of file*/
|
||||
state->readcnt=0;
|
||||
}
|
||||
req = smb_raw_read_send(state->cli,&rd);
|
||||
}
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
/*register the callback function!*/
|
||||
req->async.fn = benchrw_callback;
|
||||
req->async.private = state;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
Called when the open is done. writes to the file.
|
||||
*/
|
||||
static NTSTATUS benchrw_open(struct torture_context *tctx,struct smbcli_request *req,
|
||||
struct benchrw_state *state)
|
||||
{
|
||||
union smb_write wr;
|
||||
if(state->mode == OPEN_FILE){
|
||||
NTSTATUS status;
|
||||
status = smb_raw_open_recv(req,state->mem_ctx,(
|
||||
union smb_open*)state->req_params);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
state->fnum = ((union smb_open*)state->req_params)
|
||||
->openx.out.file.fnum;
|
||||
torture_comment(tctx, "File opened (%d)\n",state->fnum);
|
||||
state->mode=INITIAL_WRITE;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Write initial test file:%d (%d/%d)\n",state->nr,
|
||||
(state->writecnt+1)*state->lp_params->blocksize,
|
||||
(state->lp_params->writeblocks*state->lp_params->blocksize));
|
||||
wr.generic.level = RAW_WRITE_WRITEX ;
|
||||
wr.writex.in.file.fnum = state->fnum ;
|
||||
wr.writex.in.offset = state->writecnt *
|
||||
state->lp_params->blocksize;
|
||||
wr.writex.in.wmode = 0 ;
|
||||
wr.writex.in.remaining = (state->lp_params->writeblocks *
|
||||
state->lp_params->blocksize)-
|
||||
((state->writecnt+1)*state->
|
||||
lp_params->blocksize);
|
||||
wr.writex.in.count = state->lp_params->blocksize;
|
||||
wr.writex.in.data = state->buffer;
|
||||
state->writecnt++;
|
||||
if(state->writecnt == state->lp_params->writeblocks){
|
||||
state->mode=READ_WRITE_DATA;
|
||||
}
|
||||
req = smb_raw_write_send(state->cli,&wr);
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
/*register the callback function!*/
|
||||
req->async.fn = benchrw_callback;
|
||||
req->async.private = state;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
Called when the mkdir is done. Opens a file.
|
||||
*/
|
||||
static NTSTATUS benchrw_mkdir(struct torture_context *tctx,struct smbcli_request *req,
|
||||
struct benchrw_state *state)
|
||||
{
|
||||
union smb_open *open_parms;
|
||||
uint8_t *writedata;
|
||||
|
||||
NT_STATUS_NOT_OK_RETURN(req->status);
|
||||
|
||||
/* open/create the files */
|
||||
torture_comment(tctx, "Open File %d/%d\n",state->nr+1,
|
||||
lp_parm_int(-1, "torture", "nprocs", 4));
|
||||
open_parms=talloc_zero(state->mem_ctx, union smb_open);
|
||||
NT_STATUS_HAVE_NO_MEMORY(open_parms);
|
||||
open_parms->openx.level = RAW_OPEN_OPENX;
|
||||
open_parms->openx.in.flags = 0;
|
||||
open_parms->openx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
|
||||
open_parms->openx.in.search_attrs =
|
||||
FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
|
||||
open_parms->openx.in.file_attrs = 0;
|
||||
open_parms->openx.in.write_time = 0;
|
||||
open_parms->openx.in.open_func = OPENX_OPEN_FUNC_CREATE;
|
||||
open_parms->openx.in.size = 0;
|
||||
open_parms->openx.in.timeout = 0;
|
||||
open_parms->openx.in.fname = state->fname;
|
||||
|
||||
writedata = talloc_size(state->mem_ctx,state->lp_params->blocksize);
|
||||
NT_STATUS_HAVE_NO_MEMORY(writedata);
|
||||
generate_random_buffer(writedata,state->lp_params->blocksize);
|
||||
state->buffer=writedata;
|
||||
state->writecnt=1;
|
||||
state->readcnt=0;
|
||||
state->req_params=open_parms;
|
||||
state->mode=OPEN_FILE;
|
||||
|
||||
req = smb_raw_open_send(state->cli,open_parms);
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
/*register the callback function!*/
|
||||
req->async.fn = benchrw_callback;
|
||||
req->async.private = state;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
handler for completion of a sub-request of the bench-rw test
|
||||
*/
|
||||
static void benchrw_callback(struct smbcli_request *req)
|
||||
{
|
||||
struct benchrw_state *state = req->async.private;
|
||||
struct torture_context *tctx = state->tctx;
|
||||
|
||||
/*dont send new requests when torture_numops is reached*/
|
||||
if(state->completed >= torture_numops){
|
||||
state->completed=0;
|
||||
state->mode=MAX_OPS_REACHED;
|
||||
}
|
||||
|
||||
switch (state->mode) {
|
||||
|
||||
case MK_TESTDIR:
|
||||
if (!NT_STATUS_IS_OK(benchrw_mkdir(tctx, req,state))) {
|
||||
torture_comment(tctx, "Failed to create the test directory - %s\n",
|
||||
nt_errstr(req->status));
|
||||
state->mode=ERROR;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case OPEN_FILE:
|
||||
case INITIAL_WRITE:
|
||||
if (!NT_STATUS_IS_OK(benchrw_open(tctx, req,state))){
|
||||
torture_comment(tctx, "Failed to open/write the file - %s\n",
|
||||
nt_errstr(req->status));
|
||||
state->mode=ERROR;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case READ_WRITE_DATA:
|
||||
if (!NT_STATUS_IS_OK(benchrw_readwrite(tctx,req,state))){
|
||||
torture_comment(tctx, "Failed to read/write the file - %s\n",
|
||||
nt_errstr(req->status));
|
||||
state->mode=ERROR;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MAX_OPS_REACHED:
|
||||
if (!NT_STATUS_IS_OK(benchrw_close(tctx,req,state))){
|
||||
torture_comment(tctx, "Failed to read/write/close the file - %s\n",
|
||||
nt_errstr(req->status));
|
||||
state->mode=ERROR;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case CLOSE_FILE:
|
||||
torture_comment(tctx, "File %d closed\n",state->nr);
|
||||
if (!NT_STATUS_IS_OK(req->status)) {
|
||||
torture_comment(tctx, "Failed to close the file - %s\n",
|
||||
nt_errstr(req->status));
|
||||
state->mode=ERROR;
|
||||
return;
|
||||
}
|
||||
state->mode=CLEANUP;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* open connection async callback function*/
|
||||
static void async_open_callback(struct composite_context *con)
|
||||
{
|
||||
struct benchrw_state *state = con->async.private_data;
|
||||
struct torture_context *tctx = state->tctx;
|
||||
int retry = state->lp_params->retry;
|
||||
|
||||
if (NT_STATUS_IS_OK(con->status)) {
|
||||
state->cli=((struct smb_composite_connect*)
|
||||
state->req_params)->out.tree;
|
||||
state->mode=CLEANUP_TESTDIR;
|
||||
}else{
|
||||
if(state->writecnt < retry){
|
||||
torture_comment(tctx, "Failed to open connection:%d, Retry (%d/%d)\n",
|
||||
state->nr,state->writecnt,retry);
|
||||
state->writecnt++;
|
||||
state->mode=START;
|
||||
usleep(1000);
|
||||
}else{
|
||||
torture_comment(tctx, "Failed to open connection (%d) - %s\n",
|
||||
state->nr, nt_errstr(con->status));
|
||||
state->mode=ERROR;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
establishs a smbcli_tree from scratch (async)
|
||||
*/
|
||||
static struct composite_context *torture_connect_async(
|
||||
struct torture_context *tctx,
|
||||
struct smb_composite_connect *smb,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct event_context *ev,
|
||||
const char *host,
|
||||
const char *share,
|
||||
const char *workgroup)
|
||||
{
|
||||
torture_comment(tctx, "Open Connection to %s/%s\n",host,share);
|
||||
smb->in.dest_host=talloc_strdup(mem_ctx,host);
|
||||
smb->in.service=talloc_strdup(mem_ctx,share);
|
||||
smb->in.port=0;
|
||||
smb->in.called_name = strupper_talloc(mem_ctx, host);
|
||||
smb->in.service_type=NULL;
|
||||
smb->in.credentials=cmdline_credentials;
|
||||
smb->in.fallback_to_anonymous=False;
|
||||
smb->in.workgroup=workgroup;
|
||||
|
||||
return smb_composite_connect_send(smb,mem_ctx,ev);
|
||||
}
|
||||
|
||||
BOOL run_benchrw(struct torture_context *tctx)
|
||||
{
|
||||
struct smb_composite_connect *smb_con;
|
||||
const char *fname = "\\rwtest.dat";
|
||||
struct smbcli_request *req;
|
||||
struct benchrw_state **state;
|
||||
int i , num_unc_names;
|
||||
struct event_context *ev ;
|
||||
struct composite_context *req1;
|
||||
struct params lpparams;
|
||||
union smb_mkdir parms;
|
||||
int finished = 0;
|
||||
BOOL success=True;
|
||||
int torture_nprocs = lp_parm_int(-1, "torture", "nprocs", 4);
|
||||
|
||||
torture_comment(tctx, "Start BENCH-READWRITE num_ops=%d num_nprocs=%d\n",
|
||||
torture_numops, torture_nprocs);
|
||||
|
||||
/*init talloc context*/
|
||||
ev = event_context_init(tctx);
|
||||
state = talloc_array(tctx, struct benchrw_state *, torture_nprocs);
|
||||
|
||||
/* init params using lp_parm_xxx */
|
||||
num_unc_names = init_benchrw_params(tctx,&lpparams);
|
||||
|
||||
/* init private data structs*/
|
||||
for(i = 0; i<torture_nprocs;i++){
|
||||
state[i]=talloc(tctx,struct benchrw_state);
|
||||
state[i]->tctx = tctx;
|
||||
state[i]->completed=0;
|
||||
state[i]->lp_params=&lpparams;
|
||||
state[i]->nr=i;
|
||||
state[i]->dname=talloc_asprintf(tctx,"benchrw%d",i);
|
||||
state[i]->fname=talloc_asprintf(tctx,"%s%s",
|
||||
state[i]->dname,fname);
|
||||
state[i]->mode=START;
|
||||
state[i]->writecnt=0;
|
||||
}
|
||||
|
||||
torture_comment(tctx, "Starting async requests\n");
|
||||
while(finished != torture_nprocs){
|
||||
finished=0;
|
||||
for(i = 0; i<torture_nprocs;i++){
|
||||
switch (state[i]->mode){
|
||||
/*open multiple connections with the same userid */
|
||||
case START:
|
||||
smb_con = talloc(tctx,struct smb_composite_connect) ;
|
||||
state[i]->req_params=smb_con;
|
||||
state[i]->mode=OPEN_CONNECTION;
|
||||
req1 = torture_connect_async(tctx, smb_con,
|
||||
tctx,ev,
|
||||
lpparams.unc[i % num_unc_names]->host,
|
||||
lpparams.unc[i % num_unc_names]->share,
|
||||
lpparams.workgroup);
|
||||
/* register callback fn + private data */
|
||||
req1->async.fn = async_open_callback;
|
||||
req1->async.private_data=state[i];
|
||||
break;
|
||||
/*setup test dirs (sync)*/
|
||||
case CLEANUP_TESTDIR:
|
||||
torture_comment(tctx, "Setup test dir %d\n",i);
|
||||
smb_raw_exit(state[i]->cli->session);
|
||||
if (smbcli_deltree(state[i]->cli,
|
||||
state[i]->dname) == -1) {
|
||||
torture_comment(tctx, "Unable to delete %s - %s\n",
|
||||
state[i]->dname,
|
||||
smbcli_errstr(state[i]->cli));
|
||||
state[i]->mode=ERROR;
|
||||
break;
|
||||
}
|
||||
state[i]->mode=MK_TESTDIR;
|
||||
parms.mkdir.level = RAW_MKDIR_MKDIR;
|
||||
parms.mkdir.in.path = state[i]->dname;
|
||||
req = smb_raw_mkdir_send(state[i]->cli,&parms);
|
||||
/* register callback fn + private data */
|
||||
req->async.fn = benchrw_callback;
|
||||
req->async.private=state[i];
|
||||
break;
|
||||
/* error occured , finish */
|
||||
case ERROR:
|
||||
finished++;
|
||||
success=False;
|
||||
break;
|
||||
/* cleanup , close connection */
|
||||
case CLEANUP:
|
||||
torture_comment(tctx, "Deleting test dir %s %d/%d\n",state[i]->dname,
|
||||
i+1,torture_nprocs);
|
||||
smbcli_deltree(state[i]->cli,state[i]->dname);
|
||||
if (NT_STATUS_IS_ERR(smb_tree_disconnect(
|
||||
state[i]->cli))) {
|
||||
torture_comment(tctx, "ERROR: Tree disconnect failed");
|
||||
state[i]->mode=ERROR;
|
||||
break;
|
||||
}
|
||||
state[i]->mode=FINISHED;
|
||||
case FINISHED:
|
||||
finished++;
|
||||
break;
|
||||
default:
|
||||
event_loop_once(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
show server properties
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "torture/torture.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/util.h"
|
||||
|
||||
struct bitmapping {
|
||||
const char *name;
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
#define BIT_NAME(x) { #x, x }
|
||||
|
||||
const static struct bitmapping fs_attr_bits[] = {
|
||||
BIT_NAME(FS_ATTR_CASE_SENSITIVE_SEARCH),
|
||||
BIT_NAME(FS_ATTR_CASE_PRESERVED_NAMES),
|
||||
BIT_NAME(FS_ATTR_UNICODE_ON_DISK),
|
||||
BIT_NAME(FS_ATTR_PERSISTANT_ACLS),
|
||||
BIT_NAME(FS_ATTR_COMPRESSION),
|
||||
BIT_NAME(FS_ATTR_QUOTAS),
|
||||
BIT_NAME(FS_ATTR_SPARSE_FILES),
|
||||
BIT_NAME(FS_ATTR_REPARSE_POINTS),
|
||||
BIT_NAME(FS_ATTR_REMOTE_STORAGE),
|
||||
BIT_NAME(FS_ATTR_LFN_SUPPORT),
|
||||
BIT_NAME(FS_ATTR_IS_COMPRESSED),
|
||||
BIT_NAME(FS_ATTR_OBJECT_IDS),
|
||||
BIT_NAME(FS_ATTR_ENCRYPTION),
|
||||
BIT_NAME(FS_ATTR_NAMED_STREAMS),
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
const static struct bitmapping capability_bits[] = {
|
||||
BIT_NAME(CAP_RAW_MODE),
|
||||
BIT_NAME(CAP_MPX_MODE),
|
||||
BIT_NAME(CAP_UNICODE),
|
||||
BIT_NAME(CAP_LARGE_FILES),
|
||||
BIT_NAME(CAP_NT_SMBS),
|
||||
BIT_NAME(CAP_RPC_REMOTE_APIS),
|
||||
BIT_NAME(CAP_STATUS32),
|
||||
BIT_NAME(CAP_LEVEL_II_OPLOCKS),
|
||||
BIT_NAME(CAP_LOCK_AND_READ),
|
||||
BIT_NAME(CAP_NT_FIND),
|
||||
BIT_NAME(CAP_DFS),
|
||||
BIT_NAME(CAP_W2K_SMBS),
|
||||
BIT_NAME(CAP_LARGE_READX),
|
||||
BIT_NAME(CAP_LARGE_WRITEX),
|
||||
BIT_NAME(CAP_UNIX),
|
||||
BIT_NAME(CAP_EXTENDED_SECURITY),
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
static void show_bits(const struct bitmapping *bm, uint32_t value)
|
||||
{
|
||||
int i;
|
||||
for (i=0;bm[i].name;i++) {
|
||||
if (value & bm[i].value) {
|
||||
d_printf("\t%s\n", bm[i].name);
|
||||
value &= ~bm[i].value;
|
||||
}
|
||||
}
|
||||
if (value != 0) {
|
||||
d_printf("\tunknown bits: 0x%08x\n", value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
print out server properties
|
||||
*/
|
||||
BOOL torture_test_properties(struct torture_context *torture,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
BOOL correct = True;
|
||||
union smb_fsinfo fs;
|
||||
NTSTATUS status;
|
||||
|
||||
d_printf("Capabilities: 0x%08x\n", cli->transport->negotiate.capabilities);
|
||||
show_bits(capability_bits, cli->transport->negotiate.capabilities);
|
||||
d_printf("\n");
|
||||
|
||||
fs.attribute_info.level = RAW_QFS_ATTRIBUTE_INFO;
|
||||
status = smb_raw_fsinfo(cli->tree, cli, &fs);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
d_printf("qfsinfo failed - %s\n", nt_errstr(status));
|
||||
correct = False;
|
||||
} else {
|
||||
d_printf("Filesystem attributes: 0x%08x\n",
|
||||
fs.attribute_info.out.fs_attr);
|
||||
show_bits(fs_attr_bits, fs.attribute_info.out.fs_attr);
|
||||
d_printf("max_file_component_length: %d\n",
|
||||
fs.attribute_info.out.max_file_component_length);
|
||||
d_printf("fstype: %s\n", fs.attribute_info.out.fs_type.s);
|
||||
}
|
||||
|
||||
return correct;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
rename testing
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/torture.h"
|
||||
#include "torture/util.h"
|
||||
|
||||
/*
|
||||
Test rename on files open with share delete and no share delete.
|
||||
*/
|
||||
BOOL torture_test_rename(struct torture_context *tctx,
|
||||
struct smbcli_state *cli1)
|
||||
{
|
||||
const char *fname = "\\test.txt";
|
||||
const char *fname1 = "\\test1.txt";
|
||||
int fnum1;
|
||||
|
||||
smbcli_unlink(cli1->tree, fname);
|
||||
smbcli_unlink(cli1->tree, fname1);
|
||||
fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0,
|
||||
SEC_RIGHTS_FILE_READ,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NTCREATEX_SHARE_ACCESS_READ,
|
||||
NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
|
||||
|
||||
torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "First open failed - %s",
|
||||
smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert(tctx, NT_STATUS_IS_ERR(smbcli_rename(cli1->tree, fname, fname1)),
|
||||
"First rename succeeded - this should have failed !");
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1),
|
||||
talloc_asprintf(tctx, "close - 1 failed (%s)", smbcli_errstr(cli1->tree)));
|
||||
|
||||
smbcli_unlink(cli1->tree, fname);
|
||||
smbcli_unlink(cli1->tree, fname1);
|
||||
fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0,
|
||||
SEC_RIGHTS_FILE_READ,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NTCREATEX_SHARE_ACCESS_DELETE|NTCREATEX_SHARE_ACCESS_READ,
|
||||
NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
|
||||
|
||||
torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx,
|
||||
"Second open failed - %s", smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_rename(cli1->tree, fname, fname1),
|
||||
talloc_asprintf(tctx,
|
||||
"Second rename failed - this should have succeeded - %s",
|
||||
smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1),
|
||||
talloc_asprintf(tctx,
|
||||
"close - 2 failed (%s)", smbcli_errstr(cli1->tree)));
|
||||
|
||||
smbcli_unlink(cli1->tree, fname);
|
||||
smbcli_unlink(cli1->tree, fname1);
|
||||
|
||||
fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0,
|
||||
SEC_STD_READ_CONTROL,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NTCREATEX_SHARE_ACCESS_NONE,
|
||||
NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
|
||||
|
||||
torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "Third open failed - %s",
|
||||
smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_rename(cli1->tree, fname, fname1),
|
||||
talloc_asprintf(tctx, "Third rename failed - this should have succeeded - %s",
|
||||
smbcli_errstr(cli1->tree)));
|
||||
|
||||
torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1),
|
||||
talloc_asprintf(tctx, "close - 3 failed (%s)", smbcli_errstr(cli1->tree)));
|
||||
|
||||
smbcli_unlink(cli1->tree, fname);
|
||||
smbcli_unlink(cli1->tree, fname1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,558 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMB torture tester - scanning functions
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "torture/torture.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/util.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "system/filesys.h"
|
||||
#include "pstring.h"
|
||||
|
||||
#define VERBOSE 0
|
||||
#define OP_MIN 0
|
||||
#define OP_MAX 100
|
||||
|
||||
/****************************************************************************
|
||||
look for a partial hit
|
||||
****************************************************************************/
|
||||
static void trans2_check_hit(const char *format, int op, int level, NTSTATUS status)
|
||||
{
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL) ||
|
||||
NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) ||
|
||||
NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) ||
|
||||
NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) ||
|
||||
NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
|
||||
return;
|
||||
}
|
||||
#if VERBOSE
|
||||
printf("possible %s hit op=%3d level=%5d status=%s\n",
|
||||
format, op, level, nt_errstr(status));
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
check for existance of a trans2 call
|
||||
****************************************************************************/
|
||||
static NTSTATUS try_trans2(struct smbcli_state *cli,
|
||||
int op,
|
||||
uint8_t *param, uint8_t *data,
|
||||
int param_len, int data_len,
|
||||
int *rparam_len, int *rdata_len)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct smb_trans2 t2;
|
||||
uint16_t setup = op;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
mem_ctx = talloc_init("try_trans2");
|
||||
|
||||
t2.in.max_param = 64;
|
||||
t2.in.max_data = smb_raw_max_trans_data(cli->tree, 64);
|
||||
t2.in.max_setup = 10;
|
||||
t2.in.flags = 0;
|
||||
t2.in.timeout = 0;
|
||||
t2.in.setup_count = 1;
|
||||
t2.in.setup = &setup;
|
||||
t2.in.params.data = param;
|
||||
t2.in.params.length = param_len;
|
||||
t2.in.data.data = data;
|
||||
t2.in.data.length = data_len;
|
||||
|
||||
status = smb_raw_trans2(cli->tree, mem_ctx, &t2);
|
||||
|
||||
*rparam_len = t2.out.params.length;
|
||||
*rdata_len = t2.out.data.length;
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS try_trans2_len(struct smbcli_state *cli,
|
||||
const char *format,
|
||||
int op, int level,
|
||||
uint8_t *param, uint8_t *data,
|
||||
int param_len, int *data_len,
|
||||
int *rparam_len, int *rdata_len)
|
||||
{
|
||||
NTSTATUS ret=NT_STATUS_OK;
|
||||
|
||||
ret = try_trans2(cli, op, param, data, param_len,
|
||||
sizeof(pstring), rparam_len, rdata_len);
|
||||
#if VERBOSE
|
||||
printf("op=%d level=%d ret=%s\n", op, level, nt_errstr(ret));
|
||||
#endif
|
||||
if (!NT_STATUS_IS_OK(ret)) return ret;
|
||||
|
||||
*data_len = 0;
|
||||
while (*data_len < sizeof(pstring)) {
|
||||
ret = try_trans2(cli, op, param, data, param_len,
|
||||
*data_len, rparam_len, rdata_len);
|
||||
if (NT_STATUS_IS_OK(ret)) break;
|
||||
*data_len += 2;
|
||||
}
|
||||
if (NT_STATUS_IS_OK(ret)) {
|
||||
printf("found %s level=%d data_len=%d rparam_len=%d rdata_len=%d\n",
|
||||
format, level, *data_len, *rparam_len, *rdata_len);
|
||||
} else {
|
||||
trans2_check_hit(format, op, level, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
check whether a trans2 opnum exists at all
|
||||
****************************************************************************/
|
||||
static BOOL trans2_op_exists(struct smbcli_state *cli, int op)
|
||||
{
|
||||
int data_len = 0;
|
||||
int param_len = 0;
|
||||
int rparam_len, rdata_len;
|
||||
uint8_t param[1024], data[1024];
|
||||
NTSTATUS status1, status2;
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
data_len = 4;
|
||||
|
||||
/* try with a info level only */
|
||||
param_len = sizeof(param);
|
||||
data_len = sizeof(data);
|
||||
|
||||
memset(param, 0xFF, sizeof(param));
|
||||
memset(data, 0xFF, sizeof(data));
|
||||
|
||||
status1 = try_trans2(cli, 0xFFFF, param, data, param_len, data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
|
||||
status2 = try_trans2(cli, op, param, data, param_len, data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
|
||||
if (NT_STATUS_EQUAL(status1, status2)) return False;
|
||||
|
||||
printf("Found op %d (status=%s)\n", op, nt_errstr(status2));
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
check for existance of a trans2 call
|
||||
****************************************************************************/
|
||||
static BOOL scan_trans2(struct smbcli_state *cli, int op, int level,
|
||||
int fnum, int dnum, int qfnum, const char *fname)
|
||||
{
|
||||
int data_len = 0;
|
||||
int param_len = 0;
|
||||
int rparam_len, rdata_len;
|
||||
uint8_t param[1024], data[1024];
|
||||
NTSTATUS status;
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
data_len = 4;
|
||||
|
||||
/* try with a info level only */
|
||||
param_len = 2;
|
||||
SSVAL(param, 0, level);
|
||||
status = try_trans2_len(cli, "void", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
/* try with a file descriptor */
|
||||
param_len = 6;
|
||||
SSVAL(param, 0, fnum);
|
||||
SSVAL(param, 2, level);
|
||||
SSVAL(param, 4, 0);
|
||||
status = try_trans2_len(cli, "fnum", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
/* try with a quota file descriptor */
|
||||
param_len = 6;
|
||||
SSVAL(param, 0, qfnum);
|
||||
SSVAL(param, 2, level);
|
||||
SSVAL(param, 4, 0);
|
||||
status = try_trans2_len(cli, "qfnum", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
/* try with a notify style */
|
||||
param_len = 6;
|
||||
SSVAL(param, 0, dnum);
|
||||
SSVAL(param, 2, dnum);
|
||||
SSVAL(param, 4, level);
|
||||
status = try_trans2_len(cli, "notify", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
/* try with a file name */
|
||||
param_len = 6;
|
||||
SSVAL(param, 0, level);
|
||||
SSVAL(param, 2, 0);
|
||||
SSVAL(param, 4, 0);
|
||||
param_len += push_string(¶m[6], fname, sizeof(pstring)-7, STR_TERMINATE|STR_UNICODE);
|
||||
|
||||
status = try_trans2_len(cli, "fname", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
/* try with a new file name */
|
||||
param_len = 6;
|
||||
SSVAL(param, 0, level);
|
||||
SSVAL(param, 2, 0);
|
||||
SSVAL(param, 4, 0);
|
||||
param_len += push_string(¶m[6], "\\newfile.dat", sizeof(pstring)-7, STR_TERMINATE|STR_UNICODE);
|
||||
|
||||
status = try_trans2_len(cli, "newfile", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
smbcli_unlink(cli->tree, "\\newfile.dat");
|
||||
smbcli_rmdir(cli->tree, "\\newfile.dat");
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
/* try dfs style */
|
||||
smbcli_mkdir(cli->tree, "\\testdir");
|
||||
param_len = 2;
|
||||
SSVAL(param, 0, level);
|
||||
param_len += push_string(¶m[2], "\\testdir", sizeof(pstring)-3, STR_TERMINATE|STR_UNICODE);
|
||||
|
||||
status = try_trans2_len(cli, "dfs", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
smbcli_rmdir(cli->tree, "\\testdir");
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
BOOL torture_trans2_scan(struct torture_context *torture,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
int op, level;
|
||||
const char *fname = "\\scanner.dat";
|
||||
int fnum, dnum, qfnum;
|
||||
|
||||
fnum = smbcli_open(cli->tree, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
|
||||
if (fnum == -1) {
|
||||
printf("file open failed - %s\n", smbcli_errstr(cli->tree));
|
||||
}
|
||||
dnum = smbcli_nt_create_full(cli->tree, "\\",
|
||||
0,
|
||||
SEC_RIGHTS_FILE_READ,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE,
|
||||
NTCREATEX_DISP_OPEN,
|
||||
NTCREATEX_OPTIONS_DIRECTORY, 0);
|
||||
if (dnum == -1) {
|
||||
printf("directory open failed - %s\n", smbcli_errstr(cli->tree));
|
||||
}
|
||||
qfnum = smbcli_nt_create_full(cli->tree, "\\$Extend\\$Quota:$Q:$INDEX_ALLOCATION",
|
||||
NTCREATEX_FLAGS_EXTENDED,
|
||||
SEC_FLAG_MAXIMUM_ALLOWED,
|
||||
0,
|
||||
NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE,
|
||||
NTCREATEX_DISP_OPEN,
|
||||
0, 0);
|
||||
if (qfnum == -1) {
|
||||
printf("quota open failed - %s\n", smbcli_errstr(cli->tree));
|
||||
}
|
||||
|
||||
for (op=OP_MIN; op<=OP_MAX; op++) {
|
||||
|
||||
if (!trans2_op_exists(cli, op)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (level = 0; level <= 50; level++) {
|
||||
scan_trans2(cli, op, level, fnum, dnum, qfnum, fname);
|
||||
}
|
||||
|
||||
for (level = 0x100; level <= 0x130; level++) {
|
||||
scan_trans2(cli, op, level, fnum, dnum, qfnum, fname);
|
||||
}
|
||||
|
||||
for (level = 1000; level < 1050; level++) {
|
||||
scan_trans2(cli, op, level, fnum, dnum, qfnum, fname);
|
||||
}
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
look for a partial hit
|
||||
****************************************************************************/
|
||||
static void nttrans_check_hit(const char *format, int op, int level, NTSTATUS status)
|
||||
{
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL) ||
|
||||
NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) ||
|
||||
NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) ||
|
||||
NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) ||
|
||||
NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
|
||||
return;
|
||||
}
|
||||
#if VERBOSE
|
||||
printf("possible %s hit op=%3d level=%5d status=%s\n",
|
||||
format, op, level, nt_errstr(status));
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
check for existence of a nttrans call
|
||||
****************************************************************************/
|
||||
static NTSTATUS try_nttrans(struct smbcli_state *cli,
|
||||
int op,
|
||||
uint8_t *param, uint8_t *data,
|
||||
int param_len, int data_len,
|
||||
int *rparam_len, int *rdata_len)
|
||||
{
|
||||
struct smb_nttrans parms;
|
||||
DATA_BLOB ntparam_blob, ntdata_blob;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
NTSTATUS status;
|
||||
|
||||
mem_ctx = talloc_init("try_nttrans");
|
||||
|
||||
ntparam_blob.length = param_len;
|
||||
ntparam_blob.data = param;
|
||||
ntdata_blob.length = data_len;
|
||||
ntdata_blob.data = data;
|
||||
|
||||
parms.in.max_param = 64;
|
||||
parms.in.max_data = smb_raw_max_trans_data(cli->tree, 64);
|
||||
parms.in.max_setup = 0;
|
||||
parms.in.setup_count = 0;
|
||||
parms.in.function = op;
|
||||
parms.in.params = ntparam_blob;
|
||||
parms.in.data = ntdata_blob;
|
||||
|
||||
status = smb_raw_nttrans(cli->tree, mem_ctx, &parms);
|
||||
|
||||
if (NT_STATUS_IS_ERR(status)) {
|
||||
DEBUG(1,("Failed to send NT_TRANS\n"));
|
||||
talloc_free(mem_ctx);
|
||||
return status;
|
||||
}
|
||||
*rparam_len = parms.out.params.length;
|
||||
*rdata_len = parms.out.data.length;
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS try_nttrans_len(struct smbcli_state *cli,
|
||||
const char *format,
|
||||
int op, int level,
|
||||
uint8_t *param, uint8_t *data,
|
||||
int param_len, int *data_len,
|
||||
int *rparam_len, int *rdata_len)
|
||||
{
|
||||
NTSTATUS ret=NT_STATUS_OK;
|
||||
|
||||
ret = try_nttrans(cli, op, param, data, param_len,
|
||||
sizeof(pstring), rparam_len, rdata_len);
|
||||
#if VERBOSE
|
||||
printf("op=%d level=%d ret=%s\n", op, level, nt_errstr(ret));
|
||||
#endif
|
||||
if (!NT_STATUS_IS_OK(ret)) return ret;
|
||||
|
||||
*data_len = 0;
|
||||
while (*data_len < sizeof(pstring)) {
|
||||
ret = try_nttrans(cli, op, param, data, param_len,
|
||||
*data_len, rparam_len, rdata_len);
|
||||
if (NT_STATUS_IS_OK(ret)) break;
|
||||
*data_len += 2;
|
||||
}
|
||||
if (NT_STATUS_IS_OK(ret)) {
|
||||
printf("found %s level=%d data_len=%d rparam_len=%d rdata_len=%d\n",
|
||||
format, level, *data_len, *rparam_len, *rdata_len);
|
||||
} else {
|
||||
nttrans_check_hit(format, op, level, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
check for existance of a nttrans call
|
||||
****************************************************************************/
|
||||
static BOOL scan_nttrans(struct smbcli_state *cli, int op, int level,
|
||||
int fnum, int dnum, const char *fname)
|
||||
{
|
||||
int data_len = 0;
|
||||
int param_len = 0;
|
||||
int rparam_len, rdata_len;
|
||||
uint8_t param[1024], data[1024];
|
||||
NTSTATUS status;
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
data_len = 4;
|
||||
|
||||
/* try with a info level only */
|
||||
param_len = 2;
|
||||
SSVAL(param, 0, level);
|
||||
status = try_nttrans_len(cli, "void", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
/* try with a file descriptor */
|
||||
param_len = 6;
|
||||
SSVAL(param, 0, fnum);
|
||||
SSVAL(param, 2, level);
|
||||
SSVAL(param, 4, 0);
|
||||
status = try_nttrans_len(cli, "fnum", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
|
||||
/* try with a notify style */
|
||||
param_len = 6;
|
||||
SSVAL(param, 0, dnum);
|
||||
SSVAL(param, 2, dnum);
|
||||
SSVAL(param, 4, level);
|
||||
status = try_nttrans_len(cli, "notify", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
/* try with a file name */
|
||||
param_len = 6;
|
||||
SSVAL(param, 0, level);
|
||||
SSVAL(param, 2, 0);
|
||||
SSVAL(param, 4, 0);
|
||||
param_len += push_string(¶m[6], fname, sizeof(pstring), STR_TERMINATE | STR_UNICODE);
|
||||
|
||||
status = try_nttrans_len(cli, "fname", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
/* try with a new file name */
|
||||
param_len = 6;
|
||||
SSVAL(param, 0, level);
|
||||
SSVAL(param, 2, 0);
|
||||
SSVAL(param, 4, 0);
|
||||
param_len += push_string(¶m[6], "\\newfile.dat", sizeof(pstring), STR_TERMINATE | STR_UNICODE);
|
||||
|
||||
status = try_nttrans_len(cli, "newfile", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
smbcli_unlink(cli->tree, "\\newfile.dat");
|
||||
smbcli_rmdir(cli->tree, "\\newfile.dat");
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
/* try dfs style */
|
||||
smbcli_mkdir(cli->tree, "\\testdir");
|
||||
param_len = 2;
|
||||
SSVAL(param, 0, level);
|
||||
param_len += push_string(¶m[2], "\\testdir", sizeof(pstring), STR_TERMINATE | STR_UNICODE);
|
||||
|
||||
status = try_nttrans_len(cli, "dfs", op, level, param, data, param_len, &data_len,
|
||||
&rparam_len, &rdata_len);
|
||||
smbcli_rmdir(cli->tree, "\\testdir");
|
||||
if (NT_STATUS_IS_OK(status)) return True;
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
BOOL torture_nttrans_scan(struct torture_context *torture,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
int op, level;
|
||||
const char *fname = "\\scanner.dat";
|
||||
int fnum, dnum;
|
||||
|
||||
fnum = smbcli_open(cli->tree, fname, O_RDWR | O_CREAT | O_TRUNC,
|
||||
DENY_NONE);
|
||||
dnum = smbcli_open(cli->tree, "\\", O_RDONLY, DENY_NONE);
|
||||
|
||||
for (op=OP_MIN; op<=OP_MAX; op++) {
|
||||
printf("Scanning op=%d\n", op);
|
||||
for (level = 0; level <= 50; level++) {
|
||||
scan_nttrans(cli, op, level, fnum, dnum, fname);
|
||||
}
|
||||
|
||||
for (level = 0x100; level <= 0x130; level++) {
|
||||
scan_nttrans(cli, op, level, fnum, dnum, fname);
|
||||
}
|
||||
|
||||
for (level = 1000; level < 1050; level++) {
|
||||
scan_nttrans(cli, op, level, fnum, dnum, fname);
|
||||
}
|
||||
}
|
||||
|
||||
torture_close_connection(cli);
|
||||
|
||||
printf("nttrans scan finished\n");
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/* scan for valid base SMB requests */
|
||||
BOOL torture_smb_scan(struct torture_context *torture)
|
||||
{
|
||||
static struct smbcli_state *cli;
|
||||
int op;
|
||||
struct smbcli_request *req;
|
||||
NTSTATUS status;
|
||||
|
||||
for (op=0x0;op<=0xFF;op++) {
|
||||
if (op == SMBreadbraw) continue;
|
||||
|
||||
if (!torture_open_connection(&cli, 0)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
req = smbcli_request_setup(cli->tree, op, 0, 0);
|
||||
|
||||
if (!smbcli_request_send(req)) {
|
||||
smbcli_request_destroy(req);
|
||||
break;
|
||||
}
|
||||
|
||||
usleep(10000);
|
||||
smbcli_transport_process(cli->transport);
|
||||
if (req->state > SMBCLI_REQUEST_RECV) {
|
||||
status = smbcli_request_simple_recv(req);
|
||||
printf("op=0x%x status=%s\n", op, nt_errstr(status));
|
||||
torture_close_connection(cli);
|
||||
continue;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
smbcli_transport_process(cli->transport);
|
||||
if (req->state > SMBCLI_REQUEST_RECV) {
|
||||
status = smbcli_request_simple_recv(req);
|
||||
printf("op=0x%x status=%s\n", op, nt_errstr(status));
|
||||
} else {
|
||||
printf("op=0x%x no reply\n", op);
|
||||
smbcli_request_destroy(req);
|
||||
continue; /* don't attempt close! */
|
||||
}
|
||||
|
||||
torture_close_connection(cli);
|
||||
}
|
||||
|
||||
|
||||
printf("smb scan finished\n");
|
||||
return True;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
find security related memory leaks
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "torture/torture.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/util.h"
|
||||
#include "system/time.h"
|
||||
#include "libcli/smb_composite/smb_composite.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
|
||||
static BOOL try_failed_login(struct smbcli_state *cli)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct smb_composite_sesssetup setup;
|
||||
struct smbcli_session *session;
|
||||
|
||||
session = smbcli_session_init(cli->transport, cli, False);
|
||||
setup.in.sesskey = cli->transport->negotiate.sesskey;
|
||||
setup.in.capabilities = cli->transport->negotiate.capabilities;
|
||||
setup.in.workgroup = lp_workgroup();
|
||||
|
||||
setup.in.credentials = cli_credentials_init(session);
|
||||
cli_credentials_set_conf(setup.in.credentials);
|
||||
cli_credentials_set_domain(setup.in.credentials, "INVALID-DOMAIN", CRED_SPECIFIED);
|
||||
cli_credentials_set_username(setup.in.credentials, "INVALID-USERNAME", CRED_SPECIFIED);
|
||||
cli_credentials_set_password(setup.in.credentials, "INVALID-PASSWORD", CRED_SPECIFIED);
|
||||
|
||||
status = smb_composite_sesssetup(session, &setup);
|
||||
talloc_free(session);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
printf("Allowed session setup with invalid credentials?!\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
BOOL torture_sec_leak(struct torture_context *tctx, struct smbcli_state *cli)
|
||||
{
|
||||
time_t t1 = time(NULL);
|
||||
int timelimit = torture_setting_int(tctx, "timelimit", 20);
|
||||
|
||||
while (time(NULL) < t1+timelimit) {
|
||||
if (!try_failed_login(cli)) {
|
||||
return False;
|
||||
}
|
||||
talloc_report(NULL, stdout);
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
unlink tester
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "torture/torture.h"
|
||||
#include "system/filesys.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/util.h"
|
||||
|
||||
#define BASEDIR "\\unlinktest"
|
||||
|
||||
/*
|
||||
This test checks that
|
||||
|
||||
1) the server does not allow an unlink on a file that is open
|
||||
*/
|
||||
BOOL torture_unlinktest(struct torture_context *tctx, struct smbcli_state *cli)
|
||||
{
|
||||
const char *fname = BASEDIR "\\unlink.tst";
|
||||
int fnum;
|
||||
BOOL correct = True;
|
||||
union smb_open io;
|
||||
NTSTATUS status;
|
||||
|
||||
torture_assert(tctx, torture_setup_dir(cli, BASEDIR),
|
||||
talloc_asprintf(tctx, "Failed setting up %s", BASEDIR));
|
||||
|
||||
cli->session->pid = 1;
|
||||
|
||||
torture_comment(tctx, "Opening a file\n");
|
||||
|
||||
fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
|
||||
torture_assert(tctx, fnum != -1, talloc_asprintf(tctx, "open of %s failed (%s)", fname, smbcli_errstr(cli->tree)));
|
||||
|
||||
torture_comment(tctx, "Unlinking a open file\n");
|
||||
|
||||
torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_unlink(cli->tree, fname)),
|
||||
"server allowed unlink on an open file");
|
||||
|
||||
correct = check_error(__location__, cli, ERRDOS, ERRbadshare,
|
||||
NT_STATUS_SHARING_VIOLATION);
|
||||
|
||||
smbcli_close(cli->tree, fnum);
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
|
||||
torture_comment(tctx, "testing unlink after ntcreatex with DELETE access\n");
|
||||
|
||||
io.ntcreatex.level = RAW_OPEN_NTCREATEX;
|
||||
io.ntcreatex.in.root_fid = 0;
|
||||
io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
|
||||
io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
|
||||
io.ntcreatex.in.file_attr = 0;
|
||||
io.ntcreatex.in.alloc_size = 0;
|
||||
io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
|
||||
io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
||||
io.ntcreatex.in.security_flags = 0;
|
||||
io.ntcreatex.in.fname = fname;
|
||||
io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
|
||||
io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
|
||||
|
||||
status = smb_raw_open(cli->tree, cli, &io);
|
||||
torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "failed to open %s", fname));
|
||||
|
||||
torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_unlink(cli->tree, fname)),
|
||||
"server allowed unlink on an open file");
|
||||
|
||||
correct = check_error(__location__, cli, ERRDOS, ERRbadshare,
|
||||
NT_STATUS_SHARING_VIOLATION);
|
||||
|
||||
return correct;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMB torture tester - unicode table dumper
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "torture/torture.h"
|
||||
#include "system/filesys.h"
|
||||
#include "system/locale.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "torture/util.h"
|
||||
#include "pstring.h"
|
||||
|
||||
bool torture_utable(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
fstring fname;
|
||||
const char *alt_name;
|
||||
int fnum;
|
||||
uint8_t c2[4];
|
||||
int c, len, fd;
|
||||
int chars_allowed=0, alt_allowed=0;
|
||||
uint8_t valid[0x10000];
|
||||
|
||||
torture_comment(tctx, "Generating valid character table\n");
|
||||
|
||||
memset(valid, 0, sizeof(valid));
|
||||
|
||||
torture_assert(tctx, torture_setup_dir(cli, "\\utable"),
|
||||
"Setting up dir \\utable failed");
|
||||
|
||||
for (c=1; c < 0x10000; c++) {
|
||||
char *p;
|
||||
|
||||
SSVAL(c2, 0, c);
|
||||
fstrcpy(fname, "\\utable\\x");
|
||||
p = fname+strlen(fname);
|
||||
len = convert_string(CH_UTF16, CH_UNIX,
|
||||
c2, 2,
|
||||
p, sizeof(fname)-strlen(fname));
|
||||
p[len] = 0;
|
||||
fstrcat(fname,"_a_long_extension");
|
||||
|
||||
fnum = smbcli_open(cli->tree, fname, O_RDWR | O_CREAT | O_TRUNC,
|
||||
DENY_NONE);
|
||||
if (fnum == -1) continue;
|
||||
|
||||
chars_allowed++;
|
||||
|
||||
smbcli_qpathinfo_alt_name(cli->tree, fname, &alt_name);
|
||||
|
||||
if (strncmp(alt_name, "X_A_L", 5) != 0) {
|
||||
alt_allowed++;
|
||||
valid[c] = 1;
|
||||
torture_comment(tctx, "fname=[%s] alt_name=[%s]\n", fname, alt_name);
|
||||
}
|
||||
|
||||
smbcli_close(cli->tree, fnum);
|
||||
smbcli_unlink(cli->tree, fname);
|
||||
|
||||
if (c % 100 == 0) {
|
||||
torture_comment(tctx, "%d (%d/%d)\r", c, chars_allowed, alt_allowed);
|
||||
}
|
||||
}
|
||||
torture_comment(tctx, "%d (%d/%d)\n", c, chars_allowed, alt_allowed);
|
||||
|
||||
smbcli_rmdir(cli->tree, "\\utable");
|
||||
|
||||
torture_comment(tctx, "%d chars allowed %d alt chars allowed\n", chars_allowed, alt_allowed);
|
||||
|
||||
fd = open("valid.dat", O_WRONLY|O_CREAT|O_TRUNC, 0644);
|
||||
torture_assert(tctx, fd != -1,
|
||||
talloc_asprintf(tctx,
|
||||
"Failed to create valid.dat - %s", strerror(errno)));
|
||||
write(fd, valid, 0x10000);
|
||||
close(fd);
|
||||
torture_comment(tctx, "wrote valid.dat\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static char *form_name(int c)
|
||||
{
|
||||
static fstring fname;
|
||||
uint8_t c2[4];
|
||||
char *p;
|
||||
int len;
|
||||
|
||||
fstrcpy(fname, "\\utable\\");
|
||||
p = fname+strlen(fname);
|
||||
SSVAL(c2, 0, c);
|
||||
|
||||
len = convert_string(CH_UTF16, CH_UNIX,
|
||||
c2, 2,
|
||||
p, sizeof(fname)-strlen(fname));
|
||||
p[len] = 0;
|
||||
return fname;
|
||||
}
|
||||
|
||||
bool torture_casetable(struct torture_context *tctx,
|
||||
struct smbcli_state *cli)
|
||||
{
|
||||
char *fname;
|
||||
int fnum;
|
||||
int c, i;
|
||||
#define MAX_EQUIVALENCE 8
|
||||
codepoint_t equiv[0x10000][MAX_EQUIVALENCE];
|
||||
|
||||
torture_comment(tctx, "Determining upper/lower case table\n");
|
||||
|
||||
memset(equiv, 0, sizeof(equiv));
|
||||
|
||||
torture_assert(tctx, torture_setup_dir(cli, "\\utable"),
|
||||
"Error setting up dir \\utable");
|
||||
|
||||
for (c=1; c < 0x10000; c++) {
|
||||
size_t size;
|
||||
|
||||
if (c == '.' || c == '\\') continue;
|
||||
|
||||
torture_comment(tctx, "%04x (%c)\n", c, isprint(c)?c:'.');
|
||||
|
||||
fname = form_name(c);
|
||||
fnum = smbcli_nt_create_full(cli->tree, fname, 0,
|
||||
#if 0
|
||||
SEC_RIGHT_MAXIMUM_ALLOWED,
|
||||
#else
|
||||
SEC_RIGHTS_FILE_ALL,
|
||||
#endif
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NTCREATEX_SHARE_ACCESS_NONE,
|
||||
NTCREATEX_DISP_OPEN_IF, 0, 0);
|
||||
|
||||
torture_assert(tctx, fnum != -1,
|
||||
talloc_asprintf(tctx,
|
||||
"Failed to create file with char %04x\n", c));
|
||||
|
||||
size = 0;
|
||||
|
||||
if (NT_STATUS_IS_ERR(smbcli_qfileinfo(cli->tree, fnum, NULL, &size,
|
||||
NULL, NULL, NULL, NULL, NULL))) continue;
|
||||
|
||||
if (size > 0) {
|
||||
/* found a character equivalence! */
|
||||
int c2[MAX_EQUIVALENCE];
|
||||
|
||||
if (size/sizeof(int) >= MAX_EQUIVALENCE) {
|
||||
torture_comment(tctx, "too many chars match?? size=%d c=0x%04x\n",
|
||||
(int)size, c);
|
||||
smbcli_close(cli->tree, fnum);
|
||||
return False;
|
||||
}
|
||||
|
||||
smbcli_read(cli->tree, fnum, c2, 0, size);
|
||||
torture_comment(tctx, "%04x: ", c);
|
||||
equiv[c][0] = c;
|
||||
for (i=0; i<size/sizeof(int); i++) {
|
||||
torture_comment(tctx, "%04x ", c2[i]);
|
||||
equiv[c][i+1] = c2[i];
|
||||
}
|
||||
torture_comment(tctx, "\n");
|
||||
}
|
||||
|
||||
smbcli_write(cli->tree, fnum, 0, &c, size, sizeof(c));
|
||||
smbcli_close(cli->tree, fnum);
|
||||
}
|
||||
|
||||
smbcli_unlink(cli->tree, "\\utable\\*");
|
||||
smbcli_rmdir(cli->tree, "\\utable");
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user