1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-11 05:18:09 +03:00

Merge commit 'master/master' into wspp-schema

This commit is contained in:
Andrew Tridgell 2009-04-02 10:17:32 +11:00
commit 1bc9c39235
27 changed files with 1684 additions and 426 deletions

View File

@ -864,7 +864,6 @@ static int transaction_setup_recovery(struct tdb_context *tdb,
int tdb_transaction_prepare_commit(struct tdb_context *tdb)
{
const struct tdb_methods *methods;
int i;
if (tdb->transaction == NULL) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: no transaction\n"));

View File

@ -22,6 +22,7 @@
*/
#include "replace.h"
#include "system/filesys.h"
#include "system/network.h"
#include "tsocket.h"
#include "tsocket_internal.h"

View File

@ -372,12 +372,16 @@ _PUBLIC_ int idr_get_new_random(struct idr_context *idp, void *ptr, int limit)
/* first try a random starting point in the whole range, and if that fails,
then start randomly in the bottom half of the range. This can only
fail if the range is over half full */
fail if the range is over half full, and finally fallback to any
free id */
id = idr_get_new_above(idp, ptr, 1+(generate_random() % limit), limit);
if (id == -1) {
id = idr_get_new_above(idp, ptr, 1+(generate_random()%(limit/2)), limit);
}
if (id == -1) {
id = idr_get_new_above(idp, ptr, 1, limit);
}
return id;
}

View File

@ -510,6 +510,10 @@ static void cldap_reply_state_destroy(struct tevent_req *req)
static int cldap_search_state_destructor(struct cldap_search_state *s)
{
if (s->caller.cldap) {
if (s->message_id != -1) {
idr_remove(s->caller.cldap->searches.idr, s->message_id);
s->message_id = -1;
}
DLIST_REMOVE(s->caller.cldap->searches.list, s);
cldap_recvfrom_stop(s->caller.cldap);
ZERO_STRUCT(s->caller);
@ -542,8 +546,12 @@ struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
if (!req) {
return NULL;
}
ZERO_STRUCTP(state);
state->req = req;
state->caller.cldap = cldap;
state->message_id = -1;
talloc_set_destructor(state, cldap_search_state_destructor);
if (io->in.dest_address) {
if (cldap->connected) {
@ -634,7 +642,6 @@ struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
tevent_req_set_callback(subreq, cldap_search_state_queue_done, req);
DLIST_ADD_END(cldap->searches.list, state, struct cldap_search_state *);
talloc_set_destructor(state, cldap_search_state_destructor);
return req;

File diff suppressed because it is too large Load Diff

View File

@ -58,7 +58,7 @@ static void sort_sid_array_for_smbd(auth_serversupplied_info *result,
Create a UNIX user on demand.
****************************************************************************/
static int smb_create_user(const char *domain, const char *unix_username, const char *homedir)
static int _smb_create_user(const char *domain, const char *unix_username, const char *homedir)
{
TALLOC_CTX *ctx = talloc_tos();
char *add_script;
@ -1567,7 +1567,7 @@ struct passwd *smb_getpwnam( TALLOC_CTX *mem_ctx, char *domuser,
if (username[strlen(username)-1] == '$')
return NULL;
smb_create_user(NULL, username, NULL);
_smb_create_user(NULL, username, NULL);
pw = Get_Pwnam_alloc(mem_ctx, username);
}

View File

@ -116,9 +116,10 @@ AC_SUBST(LIBWBCLIENT_SHARED_TARGET)
AC_SUBST(LIBWBCLIENT_SHARED)
AC_SUBST(LIBWBCLIENT_STATIC_TARGET)
AC_SUBST(LIBWBCLIENT_STATIC)
AC_SUBST(LIBWBCLIENT_TARGET)
AC_SUBST(LIBWBCLIENT_SOVER)
AC_SUBST(LIBWBCLIENT)
AC_SUBST(WINBIND_LIBS)
AC_SUBST(LIBWBCLIENT_LIBS)
AC_SUBST(LIBSAMBAUTIL_SHARED)
@ -3920,6 +3921,10 @@ if test x"$with_ads_support" != x"no"; then
LIBS="$ac_save_LIBS"
fi
if test x"$use_ads" != xyes; then
merged_build_possible=no
fi
AC_CHECK_LIB_EXT(nscd, NSCD_LIBS, nscd_flush_cache)
PASSDB_LIBS="$PASSDB_LIBS $NSCD_LIBS"
@ -5841,8 +5846,8 @@ if test x"$HAVE_WINBIND" = x"no"; then
fi
if test x"$HAVE_WBCLIENT" = x"yes"; then
AC_CHECK_LIB(wbclient, wbcInterfaceDetails,
[WINBIND_LIBS="-lwbclient"], AC_MSG_ERROR([Could not find wbclient]), [$WBCLIENT_LDFLAGS])
WINBIND_LIBS="$WINBIND_LIBS $WBCLIENT_LDFLAGS"
[LIBWBCLIENT_LIBS="-lwbclient"], AC_MSG_ERROR([Could not find wbclient]), [$WBCLIENT_LDFLAGS])
LIBWBCLIENT_LIBS="$LIBWBCLIENT_LIBS $WBCLIENT_LDFLAGS"
AC_MSG_RESULT(yes)
AC_DEFINE(WITH_WINBIND,1,[Whether to link to wbclient])
EXTRA_BIN_PROGS="$EXTRA_BIN_PROGS bin/wbinfo\$(EXEEXT)"
@ -5861,12 +5866,15 @@ else
## Only worry about libwbclient if we have shared
# library support
LIBWBCLIENT_SHARED=$LIBWBCLIENT_SHARED_TARGET
LIBWBCLIENT_TARGET=$LIBWBCLIENT_SHARED_TARGET
LIBWBCLIENT=libwbclient
INSTALL_LIBWBCLIENT=installlibwbclient
UNINSTALL_LIBWBCLIENT=uninstalllibwbclient
WINBIND_LIBS="-lwbclient"
LIBWBCLIENT_LIBS="-lwbclient"
else
LIBWBCLIENT_STATIC=$LIBWBCLIENT_STATIC_TARGET
LIBWBCLIENT_TARGET=$LIBWBCLIENT_STATIC_TARGET
LIBWBCLIENT_LIBS=$LIBWBCLIENT_STATIC_TARGET
fi
fi
@ -6357,6 +6365,11 @@ AC_ARG_ENABLE(merged-build,
[AS_HELP_STRING([--enable-merged-build], [Build Samba 4 as well])],
[ enable_merged_build=$enableval ], [ enable_merged_build=auto ])
if test x"$enable_merged_build" = x"yes" -a \
x"$merged_build_possible" = x"no" ; then
AC_MSG_ERROR(Merged build required but not possible)
fi
m4_include(../lib/zlib/zlib.m4)
if test x$enable_merged_build = xauto; then

View File

@ -4465,6 +4465,10 @@ bool get_trust_pw_hash(const char *domain, uint8 ret_pwd[16],
const char **account_name, uint32 *channel);
struct samr_LogonHours get_logon_hours_from_pdb(TALLOC_CTX *mem_ctx,
struct samu *pw);
NTSTATUS smb_create_user(TALLOC_CTX *mem_ctx,
uint32_t acct_flags,
const char *account,
struct passwd **passwd_p);
/* The following definitions come from passdb/pdb_compat.c */

View File

@ -649,12 +649,14 @@ static NTSTATUS libnet_dssync_process(TALLOC_CTX *mem_ctx,
uint32_t dn_count;
uint32_t count;
status = ctx->ops->startup(ctx, mem_ctx, &old_utdv);
if (!NT_STATUS_IS_OK(status)) {
ctx->error_message = talloc_asprintf(ctx,
"Failed to call startup operation: %s",
nt_errstr(status));
goto out;
if (ctx->ops->startup) {
status = ctx->ops->startup(ctx, mem_ctx, &old_utdv);
if (!NT_STATUS_IS_OK(status)) {
ctx->error_message = talloc_asprintf(ctx,
"Failed to call startup operation: %s",
nt_errstr(status));
goto out;
}
}
if (ctx->single_object_replication && ctx->object_dns) {
@ -684,12 +686,14 @@ static NTSTATUS libnet_dssync_process(TALLOC_CTX *mem_ctx,
}
}
status = ctx->ops->finish(ctx, mem_ctx, pnew_utdv);
if (!NT_STATUS_IS_OK(status)) {
ctx->error_message = talloc_asprintf(ctx,
"Failed to call finishing operation: %s",
nt_errstr(status));
goto out;
if (ctx->ops->finish) {
status = ctx->ops->finish(ctx, mem_ctx, pnew_utdv);
if (!NT_STATUS_IS_OK(status)) {
ctx->error_message = talloc_asprintf(ctx,
"Failed to call finishing operation: %s",
nt_errstr(status));
goto out;
}
}
out:

View File

@ -35,6 +35,9 @@
(!(s1) && (s2)) ||\
((s1) && (s2) && (strcmp((s1), (s2)) != 0))
/****************************************************************
****************************************************************/
static NTSTATUS sam_account_from_delta(struct samu *account,
struct netr_DELTA_USER *r)
{
@ -223,72 +226,36 @@ static NTSTATUS sam_account_from_delta(struct samu *account,
return NT_STATUS_OK;
}
static NTSTATUS fetch_account_info(uint32_t rid,
/****************************************************************
****************************************************************/
static NTSTATUS fetch_account_info(TALLOC_CTX *mem_ctx,
uint32_t rid,
struct netr_DELTA_USER *r)
{
NTSTATUS nt_ret = NT_STATUS_UNSUCCESSFUL;
fstring account;
char *add_script = NULL;
struct samu *sam_account=NULL;
GROUP_MAP map;
struct group *grp;
DOM_SID user_sid;
DOM_SID group_sid;
struct passwd *passwd;
struct passwd *passwd = NULL;
fstring sid_string;
fstrcpy(account, r->account_name.string);
d_printf("Creating account: %s\n", account);
if ( !(sam_account = samu_new( NULL )) ) {
if ( !(sam_account = samu_new(mem_ctx)) ) {
return NT_STATUS_NO_MEMORY;
}
if (!(passwd = Get_Pwnam_alloc(sam_account, account))) {
/* Create appropriate user */
if (r->acct_flags & ACB_NORMAL) {
add_script = talloc_strdup(sam_account,
lp_adduser_script());
} else if ( (r->acct_flags & ACB_WSTRUST) ||
(r->acct_flags & ACB_SVRTRUST) ||
(r->acct_flags & ACB_DOMTRUST) ) {
add_script = talloc_strdup(sam_account,
lp_addmachine_script());
} else {
DEBUG(1, ("Unknown user type: %s\n",
pdb_encode_acct_ctrl(r->acct_flags, NEW_PW_FORMAT_SPACE_PADDED_LEN)));
nt_ret = NT_STATUS_UNSUCCESSFUL;
goto done;
}
if (!add_script) {
nt_ret = NT_STATUS_NO_MEMORY;
goto done;
}
if (*add_script) {
int add_ret;
add_script = talloc_all_string_sub(sam_account,
add_script,
"%u",
account);
if (!add_script) {
nt_ret = NT_STATUS_NO_MEMORY;
goto done;
}
add_ret = smbrun(add_script,NULL);
DEBUG(add_ret ? 0 : 1,("fetch_account: Running the command `%s' "
"gave %d\n", add_script, add_ret));
if (add_ret == 0) {
smb_nscd_flush_user_cache();
}
}
/* try and find the possible unix account again */
if ( !(passwd = Get_Pwnam_alloc(sam_account, account)) ) {
d_fprintf(stderr, "Could not create posix account info for '%s'\n", account);
nt_ret = NT_STATUS_NO_SUCH_USER;
goto done;
}
nt_ret = smb_create_user(sam_account, r->acct_flags, account, &passwd);
if (!NT_STATUS_IS_OK(nt_ret)) {
d_fprintf(stderr, "Could not create posix account info for '%s'\n",
account);
goto done;
}
sid_copy(&user_sid, get_global_sam_sid());
@ -349,7 +316,11 @@ static NTSTATUS fetch_account_info(uint32_t rid,
return nt_ret;
}
static NTSTATUS fetch_group_info(uint32_t rid,
/****************************************************************
****************************************************************/
static NTSTATUS fetch_group_info(TALLOC_CTX *mem_ctx,
uint32_t rid,
struct netr_DELTA_GROUP *r)
{
fstring name;
@ -410,11 +381,14 @@ static NTSTATUS fetch_group_info(uint32_t rid,
return NT_STATUS_OK;
}
static NTSTATUS fetch_group_mem_info(uint32_t rid,
/****************************************************************
****************************************************************/
static NTSTATUS fetch_group_mem_info(TALLOC_CTX *mem_ctx,
uint32_t rid,
struct netr_DELTA_GROUP_MEMBER *r)
{
int i;
TALLOC_CTX *t = NULL;
char **nt_members = NULL;
char **unix_members;
DOM_SID group_sid;
@ -440,15 +414,9 @@ static NTSTATUS fetch_group_mem_info(uint32_t rid,
d_printf("Group members of %s: ", grp->gr_name);
if (!(t = talloc_init("fetch_group_mem_info"))) {
DEBUG(0, ("could not talloc_init\n"));
return NT_STATUS_NO_MEMORY;
}
if (r->num_rids) {
if ((nt_members = TALLOC_ZERO_ARRAY(t, char *, r->num_rids)) == NULL) {
if ((nt_members = TALLOC_ZERO_ARRAY(mem_ctx, char *, r->num_rids)) == NULL) {
DEBUG(0, ("talloc failed\n"));
talloc_free(t);
return NT_STATUS_NO_MEMORY;
}
} else {
@ -459,8 +427,7 @@ static NTSTATUS fetch_group_mem_info(uint32_t rid,
struct samu *member = NULL;
DOM_SID member_sid;
if ( !(member = samu_new(t)) ) {
talloc_destroy(t);
if ( !(member = samu_new(mem_ctx)) ) {
return NT_STATUS_NO_MEMORY;
}
@ -481,7 +448,7 @@ static NTSTATUS fetch_group_mem_info(uint32_t rid,
}
d_printf("%s,", pdb_get_username(member));
nt_members[i] = talloc_strdup(t, pdb_get_username(member));
nt_members[i] = talloc_strdup(mem_ctx, pdb_get_username(member));
TALLOC_FREE(member);
}
@ -537,11 +504,14 @@ static NTSTATUS fetch_group_mem_info(uint32_t rid,
}
}
talloc_destroy(t);
return NT_STATUS_OK;
}
static NTSTATUS fetch_alias_info(uint32_t rid,
/****************************************************************
****************************************************************/
static NTSTATUS fetch_alias_info(TALLOC_CTX *mem_ctx,
uint32_t rid,
struct netr_DELTA_ALIAS *r,
const DOM_SID *dom_sid)
{
@ -599,14 +569,22 @@ static NTSTATUS fetch_alias_info(uint32_t rid,
return NT_STATUS_OK;
}
static NTSTATUS fetch_alias_mem(uint32_t rid,
/****************************************************************
****************************************************************/
static NTSTATUS fetch_alias_mem(TALLOC_CTX *mem_ctx,
uint32_t rid,
struct netr_DELTA_ALIAS_MEMBER *r,
const DOM_SID *dom_sid)
{
return NT_STATUS_OK;
}
static NTSTATUS fetch_domain_info(uint32_t rid,
/****************************************************************
****************************************************************/
static NTSTATUS fetch_domain_info(TALLOC_CTX *mem_ctx,
uint32_t rid,
struct netr_DELTA_DOMAIN *r)
{
time_t u_max_age, u_min_age, u_logout;
@ -614,7 +592,6 @@ static NTSTATUS fetch_domain_info(uint32_t rid,
const char *domname;
struct netr_AcctLockStr *lockstr = NULL;
NTSTATUS status;
TALLOC_CTX *mem_ctx = talloc_tos();
status = pull_netr_AcctLockStr(mem_ctx, &r->account_lockout,
&lockstr);
@ -683,37 +660,48 @@ static NTSTATUS fetch_domain_info(uint32_t rid,
return NT_STATUS_OK;
}
/****************************************************************
****************************************************************/
static NTSTATUS fetch_sam_entry(TALLOC_CTX *mem_ctx,
enum netr_SamDatabaseID database_id,
struct netr_DELTA_ENUM *r,
struct samsync_context *ctx)
{
switch(r->delta_type) {
NTSTATUS status = NT_STATUS_NOT_IMPLEMENTED;
switch (r->delta_type) {
case NETR_DELTA_USER:
fetch_account_info(r->delta_id_union.rid,
r->delta_union.user);
status = fetch_account_info(mem_ctx,
r->delta_id_union.rid,
r->delta_union.user);
break;
case NETR_DELTA_GROUP:
fetch_group_info(r->delta_id_union.rid,
r->delta_union.group);
status = fetch_group_info(mem_ctx,
r->delta_id_union.rid,
r->delta_union.group);
break;
case NETR_DELTA_GROUP_MEMBER:
fetch_group_mem_info(r->delta_id_union.rid,
r->delta_union.group_member);
status = fetch_group_mem_info(mem_ctx,
r->delta_id_union.rid,
r->delta_union.group_member);
break;
case NETR_DELTA_ALIAS:
fetch_alias_info(r->delta_id_union.rid,
r->delta_union.alias,
ctx->domain_sid);
status = fetch_alias_info(mem_ctx,
r->delta_id_union.rid,
r->delta_union.alias,
ctx->domain_sid);
break;
case NETR_DELTA_ALIAS_MEMBER:
fetch_alias_mem(r->delta_id_union.rid,
r->delta_union.alias_member,
ctx->domain_sid);
status = fetch_alias_mem(mem_ctx,
r->delta_id_union.rid,
r->delta_union.alias_member,
ctx->domain_sid);
break;
case NETR_DELTA_DOMAIN:
fetch_domain_info(r->delta_id_union.rid,
r->delta_union.domain);
status = fetch_domain_info(mem_ctx,
r->delta_id_union.rid,
r->delta_union.domain);
break;
/* The following types are recognised but not handled */
case NETR_DELTA_RENAME_GROUP:
@ -766,12 +754,16 @@ static NTSTATUS fetch_sam_entry(TALLOC_CTX *mem_ctx,
break;
default:
d_printf("Unknown delta record type %d\n", r->delta_type);
status = NT_STATUS_INVALID_PARAMETER;
break;
}
return NT_STATUS_OK;
return status;
}
/****************************************************************
****************************************************************/
static NTSTATUS fetch_sam_entries(TALLOC_CTX *mem_ctx,
enum netr_SamDatabaseID database_id,
struct netr_DELTA_ENUM_ARRAY *r,
@ -787,6 +779,9 @@ static NTSTATUS fetch_sam_entries(TALLOC_CTX *mem_ctx,
return NT_STATUS_OK;
}
/****************************************************************
****************************************************************/
const struct samsync_ops libnet_samsync_passdb_ops = {
.process_objects = fetch_sam_entries,
};

View File

@ -166,7 +166,7 @@ bool cli_receive_trans(struct cli_state *cli,int trans,
*data_len = *param_len = 0;
mid = SVAL(cli->inbuf,smb_mid);
mid = SVAL(cli->outbuf,smb_mid);
if (!cli_receive_smb(cli)) {
cli_state_seqnum_remove(cli, mid);
@ -487,7 +487,7 @@ bool cli_receive_nt_trans(struct cli_state *cli,
*data_len = *param_len = 0;
mid = SVAL(cli->inbuf,smb_mid);
mid = SVAL(cli->outbuf,smb_mid);
if (!cli_receive_smb(cli)) {
cli_state_seqnum_remove(cli, mid);

10
source3/m4/aclocal.m4 vendored
View File

@ -68,7 +68,8 @@ LIBUC[_SHARED_TARGET]=bin/LIBNAME.$SHLIBEXT
LIBUC[_STATIC_TARGET]=bin/LIBNAME.a
LIBUC[_SHARED]=
LIBUC[_STATIC]=
LIBUC[_LIBS]=
LIBUC[_LIBS]=LIBLIBS
LIBUC[_TARGET]=
[INSTALL_]LIBUC=
[UNINSTALL_]LIBUC=
@ -79,6 +80,7 @@ AC_SUBST(LIBUC[_STATIC_TARGET])
AC_SUBST(LIBUC[_SHARED])
AC_SUBST(LIBUC[_STATIC])
AC_SUBST(LIBUC[_LIBS])
AC_SUBST(LIBUC[_TARGET])
AC_SUBST([INSTALL_]LIBUC)
AC_SUBST([UNINSTALL_]LIBUC)
AC_SUBST(LIBUC[_SOVER])
@ -137,14 +139,18 @@ if eval test x"$build_lib" = "xyes" ; then
[UNINSTALL_]LIBUC=[uninstall]LIBNAME
if eval $BLDSHARED = true; then
LIBUC[_SHARED]=$LIBUC[_SHARED_TARGET]
LIBUC[_TARGET]=$LIBUC[_SHARED_TARGET]
AC_MSG_RESULT(yes)
if test x"$USESHARED" != x"true" -o x"$[LINK_]LIBUC" = "xSTATIC" ; then
enable_static=yes
LIBUC[_TARGET]=$LIBUC[_STATIC_TARGET]
LIBUC[_LIBS]=$LIBUC[_STATIC_TARGET]
else
LIBUC[_LIBS]=LIBLIBS
fi
else
enable_static=yes
LIBUC[_TARGET]=$LIBUC[_STATIC_TARGET]
AC_MSG_RESULT(no shared library support -- will supply static library)
fi
else
@ -152,7 +158,7 @@ else
AC_MSG_RESULT(shared library not selected, but will supply static library)
fi
if test $enable_static = yes; then
LIBUC[_STATIC]=[\$\(]LIBUC[_OBJ0\)]
LIBUC[_STATIC]=$LIBUC[_STATIC_TARGET]
fi
m4_popdef([LIBNAME])

View File

@ -324,7 +324,7 @@ NTSTATUS onefs_brl_lock_windows(vfs_handle_struct *handle,
id = onefs_get_new_id();
}
DEBUG(10, ("Calling ifs_cbrl(LOCK)..."));
DEBUG(10, ("Calling ifs_cbrl(LOCK)...\n"));
error = ifs_cbrl(fd, CBRL_OP_LOCK, type, plock->start,
plock->size, async, id, plock->context.smbpid, plock->context.tid,
plock->fnum);
@ -388,7 +388,7 @@ bool onefs_brl_unlock_windows(vfs_handle_struct *handle,
SMB_ASSERT(plock->lock_flav == WINDOWS_LOCK);
SMB_ASSERT(plock->lock_type == UNLOCK_LOCK);
DEBUG(10, ("Calling ifs_cbrl(UNLOCK)..."));
DEBUG(10, ("Calling ifs_cbrl(UNLOCK)...\n"));
error = ifs_cbrl(fd, CBRL_OP_UNLOCK, CBRL_LK_SH,
plock->start, plock->size, false, 0, plock->context.smbpid,
plock->context.tid, plock->fnum);
@ -432,9 +432,9 @@ bool onefs_brl_cancel_windows(vfs_handle_struct *handle,
bs = ((struct onefs_cbrl_blr_state *)blr->blr_private);
SMB_ASSERT(bs);
if (bs->state == ONEFS_CBRL_DONE) {
if (bs->state == ONEFS_CBRL_DONE || bs->state == ONEFS_CBRL_ERROR) {
/* No-op. */
DEBUG(10, ("State=DONE, returning true\n"));
DEBUG(10, ("State=%d, returning true\n", bs->state));
END_PROFILE(syscall_brl_cancel);
return true;
}
@ -443,7 +443,7 @@ bool onefs_brl_cancel_windows(vfs_handle_struct *handle,
bs->state == ONEFS_CBRL_ASYNC);
/* A real cancel. */
DEBUG(10, ("Calling ifs_cbrl(CANCEL)..."));
DEBUG(10, ("Calling ifs_cbrl(CANCEL)...\n"));
error = ifs_cbrl(fd, CBRL_OP_CANCEL, CBRL_LK_UNSPEC, plock->start,
plock->size, false, bs->id, plock->context.smbpid,
plock->context.tid, plock->fnum);

View File

@ -234,7 +234,7 @@ void onefs_sys_config_enc(void)
ret = enc_set_proc(ENC_UTF8);
if (ret) {
DEBUG(0, ("Setting process encoding failed: %s",
DEBUG(0, ("Setting process encoding failed: %s\n",
strerror(errno)));
}
}
@ -256,7 +256,7 @@ void onefs_sys_config_snap_opt(struct onefs_vfs_global_config *global_config)
ret = ifs_set_dotsnap_options(&dso);
if (ret) {
DEBUG(0, ("Setting snapshot visibility/accessibility "
"failed: %s", strerror(errno)));
"failed: %s\n", strerror(errno)));
}
}
@ -270,7 +270,7 @@ void onefs_sys_config_tilde(struct onefs_vfs_global_config *global_config)
ret = ifs_tilde_snapshot(global_config->dot_snap_tilde);
if (ret) {
DEBUG(0, ("Setting snapshot tilde failed: %s",
DEBUG(0, ("Setting snapshot tilde failed: %s\n",
strerror(errno)));
}
}

View File

@ -207,10 +207,11 @@ static NTSTATUS onefs_open_file(files_struct *fsp,
*/
if ((oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK) !=
NO_OPLOCK) {
DEBUG(0,("Oplock(%d) being requested on a stream! "
"Ignoring oplock request: base=%s, stream=%s\n",
oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK,
base, stream));
DEBUG(0, ("Oplock(%d) being requested on a stream! "
"Ignoring oplock request: base=%s, "
"stream=%s\n",
oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK,
base, stream));
/* Recover by requesting NO_OPLOCK instead. */
oplock_request &= SAMBA_PRIVATE_OPLOCK_MASK;
}

View File

@ -110,7 +110,7 @@ int onefs_sys_create_file(connection_struct *conn,
status = onefs_samba_sd_to_sd(secinfo, sd, &ifs_sd, SNUM(conn));
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1, ("SD initialization failure: %s",
DEBUG(1, ("SD initialization failure: %s\n",
nt_errstr(status)));
errno = EINVAL;
goto out;
@ -162,11 +162,11 @@ int onefs_sys_create_file(connection_struct *conn,
open_access_mask));
}
DEBUG(10,("onefs_sys_create_file: base_fd = %d, "
DEBUG(10,("onefs_sys_create_file: base_fd = %d, fname = %s"
"open_access_mask = 0x%x, flags = 0x%x, mode = 0%o, "
"desired_oplock = %s, id = 0x%x, secinfo = 0x%x, sd = %p, "
"dos_attributes = 0x%x, path = %s, "
"default_acl=%s\n", base_fd,
"default_acl=%s\n", base_fd, path,
(unsigned int)open_access_mask,
(unsigned int)flags,
(unsigned int)mode,
@ -328,7 +328,7 @@ ssize_t onefs_sys_sendfile(connection_struct *conn, int tofd, int fromfd,
/* If the sendfile wasn't atomic, we're done. */
if (!atomic) {
DEBUG(10, ("non-atomic sendfile read %ul bytes", ret));
DEBUG(10, ("non-atomic sendfile read %ul bytes\n", ret));
END_PROFILE(syscall_sendfile);
return ret;
}
@ -418,7 +418,7 @@ ssize_t onefs_sys_sendfile(connection_struct *conn, int tofd, int fromfd,
}
if (count < 0x10000) {
DEBUG(0, ("Count < 2^16 and E2BIG was returned! %lu",
DEBUG(0, ("Count < 2^16 and E2BIG was returned! %lu\n",
count));
}

View File

@ -2348,3 +2348,62 @@ struct samr_LogonHours get_logon_hours_from_pdb(TALLOC_CTX *mem_ctx,
return hours;
}
/****************************************************************
****************************************************************/
NTSTATUS smb_create_user(TALLOC_CTX *mem_ctx,
uint32_t acct_flags,
const char *account,
struct passwd **passwd_p)
{
struct passwd *passwd;
char *add_script = NULL;
passwd = Get_Pwnam_alloc(mem_ctx, account);
if (passwd) {
*passwd_p = passwd;
return NT_STATUS_OK;
}
/* Create appropriate user */
if (acct_flags & ACB_NORMAL) {
add_script = talloc_strdup(mem_ctx, lp_adduser_script());
} else if ( (acct_flags & ACB_WSTRUST) ||
(acct_flags & ACB_SVRTRUST) ||
(acct_flags & ACB_DOMTRUST) ) {
add_script = talloc_strdup(mem_ctx, lp_addmachine_script());
} else {
DEBUG(1, ("Unknown user type: %s\n",
pdb_encode_acct_ctrl(acct_flags, NEW_PW_FORMAT_SPACE_PADDED_LEN)));
return NT_STATUS_UNSUCCESSFUL;
}
if (!add_script) {
return NT_STATUS_NO_MEMORY;
}
if (*add_script) {
int add_ret;
add_script = talloc_all_string_sub(mem_ctx, add_script,
"%u", account);
if (!add_script) {
return NT_STATUS_NO_MEMORY;
}
add_ret = smbrun(add_script, NULL);
DEBUG(add_ret ? 0 : 1,("fetch_account: Running the command `%s' "
"gave %d\n", add_script, add_ret));
if (add_ret == 0) {
smb_nscd_flush_user_cache();
}
}
/* try and find the possible unix account again */
passwd = Get_Pwnam_alloc(mem_ctx, account);
if (!passwd) {
return NT_STATUS_NO_SUCH_USER;
}
*passwd_p = passwd;
return NT_STATUS_OK;
}

View File

@ -250,6 +250,11 @@ static bool tdbsam_convert_backup(const char *dbname, struct db_context **pp_db)
smb_panic("tdbsam_convert_backup: orig commit failed\n");
}
/* be sure to close the DBs _before_ renaming the file */
TALLOC_FREE(orig_db);
TALLOC_FREE(tmp_db);
/* This is safe from other users as we know we're
* under a mutex here. */
@ -262,13 +267,22 @@ static bool tdbsam_convert_backup(const char *dbname, struct db_context **pp_db)
}
TALLOC_FREE(frame);
TALLOC_FREE(orig_db);
/* re-open the converted TDB */
orig_db = db_open(NULL, dbname, 0,
TDB_DEFAULT, O_CREAT|O_RDWR, 0600);
if (orig_db == NULL) {
DEBUG(0, ("tdbsam_convert_backup: Failed to re-open "
"converted passdb TDB [%s]\n", dbname));
return false;
}
DEBUG(1, ("tdbsam_convert_backup: updated %s file.\n",
dbname ));
/* Replace the global db pointer. */
*pp_db = tmp_db;
*pp_db = orig_db;
return true;
cancel:

View File

@ -300,7 +300,7 @@ static int net_conf_import(struct net_context *c, struct smbconf_ctx *conf_ctx,
net_conf_import_usage(c, argc, argv);
goto done;
case 2:
servicename = talloc_strdup_lower(mem_ctx, argv[1]);
servicename = talloc_strdup(mem_ctx, argv[1]);
if (servicename == NULL) {
d_printf("error: out of memory!\n");
goto done;
@ -501,7 +501,7 @@ static int net_conf_showshare(struct net_context *c,
goto done;
}
sharename = talloc_strdup_lower(mem_ctx, argv[0]);
sharename = talloc_strdup(mem_ctx, argv[0]);
if (sharename == NULL) {
d_printf("error: out of memory!\n");
goto done;
@ -514,7 +514,7 @@ static int net_conf_showshare(struct net_context *c,
goto done;
}
d_printf("[%s]\n", sharename);
d_printf("[%s]\n", service->name);
for (count = 0; count < service->num_params; count++) {
d_printf("\t%s = %s\n", service->param_names[count],
@ -600,7 +600,7 @@ static int net_conf_addshare(struct net_context *c,
}
case 2:
path = argv[1];
sharename = talloc_strdup_lower(mem_ctx, argv[0]);
sharename = talloc_strdup(mem_ctx, argv[0]);
if (sharename == NULL) {
d_printf("error: out of memory!\n");
goto done;
@ -728,7 +728,7 @@ static int net_conf_delshare(struct net_context *c,
net_conf_delshare_usage(c, argc, argv);
goto done;
}
sharename = talloc_strdup_lower(mem_ctx, argv[0]);
sharename = talloc_strdup(mem_ctx, argv[0]);
if (sharename == NULL) {
d_printf("error: out of memory!\n");
goto done;
@ -761,7 +761,7 @@ static int net_conf_setparm(struct net_context *c, struct smbconf_ctx *conf_ctx,
net_conf_setparm_usage(c, argc, argv);
goto done;
}
service = talloc_strdup_lower(mem_ctx, argv[0]);
service = talloc_strdup(mem_ctx, argv[0]);
if (service == NULL) {
d_printf("error: out of memory!\n");
goto done;
@ -813,7 +813,7 @@ static int net_conf_getparm(struct net_context *c, struct smbconf_ctx *conf_ctx,
net_conf_getparm_usage(c, argc, argv);
goto done;
}
service = talloc_strdup_lower(mem_ctx, argv[0]);
service = talloc_strdup(mem_ctx, argv[0]);
if (service == NULL) {
d_printf("error: out of memory!\n");
goto done;
@ -863,7 +863,7 @@ static int net_conf_delparm(struct net_context *c, struct smbconf_ctx *conf_ctx,
net_conf_delparm_usage(c, argc, argv);
goto done;
}
service = talloc_strdup_lower(mem_ctx, argv[0]);
service = talloc_strdup(mem_ctx, argv[0]);
if (service == NULL) {
d_printf("error: out of memory!\n");
goto done;
@ -916,7 +916,7 @@ static int net_conf_getincludes(struct net_context *c,
goto done;
}
service = talloc_strdup_lower(mem_ctx, argv[0]);
service = talloc_strdup(mem_ctx, argv[0]);
if (service == NULL) {
d_printf("error: out of memory!\n");
goto done;
@ -956,7 +956,7 @@ static int net_conf_setincludes(struct net_context *c,
goto done;
}
service = talloc_strdup_lower(mem_ctx, argv[0]);
service = talloc_strdup(mem_ctx, argv[0]);
if (service == NULL) {
d_printf("error: out of memory!\n");
goto done;
@ -996,7 +996,7 @@ static int net_conf_delincludes(struct net_context *c,
goto done;
}
service = talloc_strdup_lower(mem_ctx, argv[0]);
service = talloc_strdup(mem_ctx, argv[0]);
if (service == NULL) {
d_printf("error: out of memory!\n");
goto done;

View File

@ -56,13 +56,26 @@ struct smb2_handle {
/*
SMB2 lease structure (per MS-SMB2 2.2.13)
*/
struct smb2_lease_key {
uint64_t data[2];
};
struct smb2_lease {
uint64_t lease_key[2];
struct smb2_lease_key lease_key;
uint32_t lease_state;
uint32_t lease_flags; /* should be 0 */
uint64_t lease_duration; /* should be 0 */
};
struct smb2_lease_break {
struct smb2_lease current_lease;
uint32_t break_flags;
uint32_t new_lease_state;
uint32_t break_reason; /* should be 0 */
uint32_t access_mask_hint; /* should be 0 */
uint32_t share_mask_hint; /* should be 0 */
};
struct ntvfs_handle;
/*
@ -2006,6 +2019,14 @@ union smb_lock {
/* struct smb2_handle handle; */
} in, out;
} smb2_break;
/* SMB2 Lease Break Ack (same opcode as smb2_break) */
struct smb2_lease_break_ack {
struct {
uint32_t reserved;
struct smb2_lease lease;
} in, out;
} smb2_lease_break_ack;
};

View File

@ -5,6 +5,7 @@ LIBCLI_SMB2_OBJ_FILES = $(addprefix $(libclisrcdir)/smb2/, \
transport.o request.o negprot.o session.o tcon.o \
create.o close.o connect.o getinfo.o write.o read.o \
setinfo.o find.o ioctl.o logoff.o tdis.o flush.o \
lock.o notify.o cancel.o keepalive.o break.o util.o signing.o)
lock.o notify.o cancel.o keepalive.o break.o util.o signing.o \
lease_break.o)
$(eval $(call proto_header_template,$(libclisrcdir)/smb2/smb2_proto.h,$(LIBCLI_SMB2_OBJ_FILES:.o=.c)))

View File

@ -315,7 +315,7 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create
if (io->in.lease_request) {
uint8_t data[32];
memcpy(&data[0], io->in.lease_request->lease_key, 16);
memcpy(&data[0], &io->in.lease_request->lease_key, 16);
SIVAL(data, 16, io->in.lease_request->lease_state);
SIVAL(data, 20, io->in.lease_request->lease_flags);
SBVAL(data, 24, io->in.lease_request->lease_duration);
@ -427,7 +427,7 @@ NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct
}
data = io->out.blobs.blobs[i].data.data;
memcpy(io->out.lease_response.lease_key, data, 16);
memcpy(&io->out.lease_response.lease_key, data, 16);
io->out.lease_response.lease_state = IVAL(data, 16);
io->out.lease_response.lease_flags = IVAL(data, 20);
io->out.lease_response.lease_duration = BVAL(data, 24);

View File

@ -0,0 +1,81 @@
/*
Unix SMB/CIFS implementation.
SMB2 client oplock break handling
Copyright (C) Zachary Loafman 2009
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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "libcli/smb2/smb2.h"
#include "libcli/smb2/smb2_calls.h"
/*
Send a Lease Break Acknowledgement
*/
struct smb2_request *smb2_lease_break_ack_send(struct smb2_tree *tree,
struct smb2_lease_break_ack *io)
{
struct smb2_request *req;
req = smb2_request_init_tree(tree, SMB2_OP_BREAK, 0x24, false, 0);
if (req == NULL) return NULL;
SIVAL(req->out.body, 0x02, io->in.reserved);
SIVAL(req->out.body, 0x04, io->in.lease.lease_flags);
memcpy(req->out.body+0x8, &io->in.lease.lease_key,
sizeof(struct smb2_lease_key));
SIVAL(req->out.body, 0x18, io->in.lease.lease_state);
SBVAL(req->out.body, 0x1C, io->in.lease.lease_duration);
smb2_transport_send(req);
return req;
}
/*
Receive a Lease Break Response
*/
NTSTATUS smb2_lease_break_ack_recv(struct smb2_request *req,
struct smb2_lease_break_ack *io)
{
if (!smb2_request_receive(req) ||
!smb2_request_is_ok(req)) {
return smb2_request_destroy(req);
}
SMB2_CHECK_PACKET_RECV(req, 0x24, false);
io->out.reserved = IVAL(req->in.body, 0x02);
io->out.lease.lease_flags = IVAL(req->in.body, 0x04);
memcpy(&io->out.lease.lease_key, req->in.body+0x8,
sizeof(struct smb2_lease_key));
io->out.lease.lease_state = IVAL(req->in.body, 0x18);
io->out.lease.lease_duration = IVAL(req->in.body, 0x1C);
return smb2_request_destroy(req);
}
/*
sync flush request
*/
NTSTATUS smb2_lease_break_ack(struct smb2_tree *tree,
struct smb2_lease_break_ack *io)
{
struct smb2_request *req = smb2_lease_break_ack_send(tree, io);
return smb2_lease_break_ack_recv(req, io);
}

View File

@ -26,6 +26,7 @@
#include "libcli/raw/libcliraw.h"
struct smb2_handle;
struct smb2_lease_break;
/*
information returned from the negotiate process
@ -73,6 +74,15 @@ struct smb2_transport {
void *private_data;
} oplock;
struct {
/* a lease break request handler */
bool (*handler)(struct smb2_transport *transport,
const struct smb2_lease_break *lease_break,
void *private_data);
/* private data passed to the oplock handler */
void *private_data;
} lease;
struct smbcli_options options;
bool signing_required;
@ -271,6 +281,9 @@ struct smb2_request {
#define SMB2_LEASE_HANDLE 0x02
#define SMB2_LEASE_WRITE 0x04
/* SMB2 lease break flags */
#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED 0x01
/* SMB2 impersonation levels */
#define SMB2_IMPERSONATION_ANONYMOUS 0x00
#define SMB2_IMPERSONATION_IDENTIFICATION 0x01

View File

@ -144,24 +144,39 @@ static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport,
const DATA_BLOB *blob)
{
uint8_t *hdr;
uint16_t opcode;
uint8_t *body;
uint16_t len, bloblen;
bool lease;
hdr = blob->data+NBT_HDR_SIZE;
body = hdr+SMB2_HDR_BODY;
bloblen = blob->length - SMB2_HDR_BODY;
if (blob->length < (SMB2_MIN_SIZE+0x18)) {
if (bloblen < 2) {
DEBUG(1,("Discarding smb2 oplock reply of size %u\n",
(unsigned)blob->length));
(unsigned)blob->length));
return NT_STATUS_INVALID_NETWORK_RESPONSE;
}
opcode = SVAL(hdr, SMB2_HDR_OPCODE);
if (opcode != SMB2_OP_BREAK) {
len = CVAL(body, 0x00);
if (len > bloblen) {
DEBUG(1,("Discarding smb2 oplock reply,"
"packet claims %u byte body, only %u bytes seen\n",
len, bloblen));
return NT_STATUS_INVALID_NETWORK_RESPONSE;
}
if (transport->oplock.handler) {
uint8_t *body = hdr+SMB2_HDR_BODY;
if (len == 24) {
lease = false;
} else if (len == 44) {
lease = true;
} else {
DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n",
(unsigned)blob->length));
return NT_STATUS_INVALID_NETWORK_RESPONSE;
}
if (!lease && transport->oplock.handler) {
struct smb2_handle h;
uint8_t level;
@ -170,8 +185,24 @@ static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport,
transport->oplock.handler(transport, &h, level,
transport->oplock.private_data);
} else if (lease && transport->lease.handler) {
struct smb2_lease_break lb;
ZERO_STRUCT(lb);
lb.break_flags = SVAL(body, 0x4);
memcpy(&lb.current_lease.lease_key, body+0x8,
sizeof(struct smb2_lease_key));
lb.current_lease.lease_state = SVAL(body, 0x18);
lb.new_lease_state = SVAL(body, 0x1C);
lb.break_reason = SVAL(body, 0x20);
lb.access_mask_hint = SVAL(body, 0x24);
lb.share_mask_hint = SVAL(body, 0x28);
transport->lease.handler(transport, &lb,
transport->lease.private_data);
} else {
DEBUG(5,("Got SMB2 oplock break with no handler\n"));
DEBUG(5,("Got SMB2 %s break with no handler\n",
lease ? "lease" : "oplock"));
}
return NT_STATUS_OK;
@ -193,6 +224,7 @@ static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob)
uint16_t buffer_code;
uint32_t dynamic_size;
uint32_t i;
uint16_t opcode;
NTSTATUS status;
buffer = blob.data;
@ -207,9 +239,16 @@ static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob)
flags = IVAL(hdr, SMB2_HDR_FLAGS);
seqnum = BVAL(hdr, SMB2_HDR_MESSAGE_ID);
opcode = SVAL(hdr, SMB2_HDR_OPCODE);
/* see MS-SMB2 3.2.5.19 */
if (seqnum == UINT64_MAX) {
if (opcode != SMB2_OP_BREAK) {
DEBUG(1,("Discarding packet with invalid seqnum, "
"opcode %u\n", opcode));
return NT_STATUS_INVALID_NETWORK_RESPONSE;
}
return smb2_handle_oplock_break(transport, &blob);
}

View File

@ -41,6 +41,15 @@
goto done; \
}} while (0)
#define CHECK_CREATED(__io, __created, __attribute) \
do { \
CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
CHECK_VAL((__io)->out.alloc_size, 0); \
CHECK_VAL((__io)->out.size, 0); \
CHECK_VAL((__io)->out.file_attr, (__attribute)); \
CHECK_VAL((__io)->out.reserved2, 0); \
} while(0)
/*
basic testing of SMB2 durable opens
regarding the position information on the handle
@ -54,7 +63,6 @@ bool test_durable_open_file_position(struct torture_context *tctx,
struct smb2_create io1, io2;
NTSTATUS status;
const char *fname = "durable_open_position.dat";
DATA_BLOB b;
union smb_fileinfo qfinfo;
union smb_setfileinfo sfinfo;
bool ret = true;
@ -78,31 +86,17 @@ bool test_durable_open_file_position(struct torture_context *tctx,
NTCREATEX_OPTIONS_ASYNC_ALERT |
NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
0x00200000;
io1.in.durable_open = true;
io1.in.fname = fname;
b = data_blob_talloc(mem_ctx, NULL, 16);
SBVAL(b.data, 0, 0);
SBVAL(b.data, 8, 0);
status = smb2_create_blob_add(tree1, &io1.in.blobs,
SMB2_CREATE_TAG_DHNQ,
b);
CHECK_STATUS(status, NT_STATUS_OK);
status = smb2_create(tree1, mem_ctx, &io1);
CHECK_STATUS(status, NT_STATUS_OK);
h1 = io1.out.file.handle;
CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
/*CHECK_VAL(io1.out.reserved, 0);*/
CHECK_VAL(io1.out.create_action, NTCREATEX_ACTION_CREATED);
CHECK_VAL(io1.out.alloc_size, 0);
CHECK_VAL(io1.out.size, 0);
CHECK_VAL(io1.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io1.out.reserved2, 0);
/* TODO: check extra blob content */
h1 = io1.out.file.handle;
ZERO_STRUCT(qfinfo);
qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
qfinfo.generic.in.file.handle = h1;
@ -141,15 +135,7 @@ bool test_durable_open_file_position(struct torture_context *tctx,
ZERO_STRUCT(io2);
io2.in.fname = fname;
b = data_blob_talloc(tctx, NULL, 16);
SBVAL(b.data, 0, h1.data[0]);
SBVAL(b.data, 8, h1.data[1]);
status = smb2_create_blob_add(tree2, &io2.in.blobs,
SMB2_CREATE_TAG_DHNC,
b);
CHECK_STATUS(status, NT_STATUS_OK);
io2.in.durable_handle = &h1;
status = smb2_create(tree2, mem_ctx, &io2);
CHECK_STATUS(status, NT_STATUS_OK);
@ -191,12 +177,14 @@ bool test_durable_open_oplock(struct torture_context *tctx,
{
TALLOC_CTX *mem_ctx = talloc_new(tctx);
struct smb2_create io1, io2;
struct smb2_handle h1;
struct smb2_handle h1, h2;
NTSTATUS status;
const char *fname = "durable_open_oplock.dat";
DATA_BLOB b;
char fname[256];
bool ret = true;
/* Choose a random name in case the state is left a little funky. */
snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
/* Clean slate */
smb2_util_unlink(tree1, fname);
@ -218,29 +206,16 @@ bool test_durable_open_oplock(struct torture_context *tctx,
NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
0x00200000;
io1.in.fname = fname;
io1.in.durable_open = true;
io2 = io1;
io2.in.create_disposition = NTCREATEX_DISP_OPEN;
b = data_blob_talloc(mem_ctx, NULL, 16);
SBVAL(b.data, 0, 0);
SBVAL(b.data, 8, 0);
status = smb2_create_blob_add(tree1, &io1.in.blobs,
SMB2_CREATE_TAG_DHNQ,
b);
CHECK_STATUS(status, NT_STATUS_OK);
status = smb2_create(tree1, mem_ctx, &io1);
CHECK_STATUS(status, NT_STATUS_OK);
CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
CHECK_VAL(io1.out.create_action, NTCREATEX_ACTION_CREATED);
CHECK_VAL(io1.out.alloc_size, 0);
CHECK_VAL(io1.out.size, 0);
CHECK_VAL(io1.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io1.out.reserved2, 0);
h1 = io1.out.file.handle;
CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
/* Disconnect after getting the batch */
talloc_free(tree1);
@ -253,12 +228,9 @@ bool test_durable_open_oplock(struct torture_context *tctx,
*/
status = smb2_create(tree2, mem_ctx, &io2);
CHECK_STATUS(status, NT_STATUS_OK);
h2 = io2.out.file.handle;
CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
CHECK_VAL(io2.out.alloc_size, 0);
CHECK_VAL(io2.out.size, 0);
CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io2.out.reserved2, 0);
/* What if tree1 tries to come back and reclaim? */
if (!torture_smb2_connection(tctx, &tree1)) {
@ -267,24 +239,349 @@ bool test_durable_open_oplock(struct torture_context *tctx,
goto done;
}
ZERO_STRUCT(io2);
io2.in.fname = fname;
ZERO_STRUCT(io1);
io1.in.fname = fname;
io1.in.durable_handle = &h1;
b = data_blob_talloc(tctx, NULL, 16);
SBVAL(b.data, 0, h1.data[0]);
SBVAL(b.data, 8, h1.data[1]);
status = smb2_create_blob_add(tree2, &io2.in.blobs,
SMB2_CREATE_TAG_DHNC,
b);
CHECK_STATUS(status, NT_STATUS_OK);
status = smb2_create(tree2, mem_ctx, &io2);
status = smb2_create(tree1, mem_ctx, &io1);
CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
done:
return ret;
done:
smb2_util_close(tree2, h2);
smb2_util_unlink(tree2, fname);
return ret;
}
/*
Open, disconnect, lease break, reconnect.
*/
bool test_durable_open_lease(struct torture_context *tctx,
struct smb2_tree *tree1,
struct smb2_tree *tree2)
{
TALLOC_CTX *mem_ctx = talloc_new(tctx);
struct smb2_create io1, io2;
struct smb2_lease ls1, ls2;
struct smb2_handle h1, h2;
NTSTATUS status;
char fname[256];
bool ret = true;
uint64_t lease1, lease2;
/*
* Choose a random name and random lease in case the state is left a
* little funky.
*/
lease1 = random();
lease2 = random();
snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
/* Clean slate */
smb2_util_unlink(tree1, fname);
/* Create with lease */
ZERO_STRUCT(io1);
io1.in.security_flags = 0x00;
io1.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
io1.in.create_flags = 0x00000000;
io1.in.reserved = 0x00000000;
io1.in.desired_access = SEC_RIGHTS_FILE_ALL;
io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
io1.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
NTCREATEX_SHARE_ACCESS_WRITE |
NTCREATEX_SHARE_ACCESS_DELETE;
io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
NTCREATEX_OPTIONS_ASYNC_ALERT |
NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
0x00200000;
io1.in.fname = fname;
io1.in.durable_open = true;
ZERO_STRUCT(ls1);
ls1.lease_key.data[0] = lease1;
ls1.lease_key.data[1] = ~lease1;
ls1.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE;
io1.in.lease_request = &ls1;
io2 = io1;
ls2 = ls1;
ls2.lease_key.data[0] = lease2;
ls2.lease_key.data[1] = ~lease2;
io2.in.lease_request = &ls2;
io2.in.create_disposition = NTCREATEX_DISP_OPEN;
status = smb2_create(tree1, mem_ctx, &io1);
CHECK_STATUS(status, NT_STATUS_OK);
h1 = io1.out.file.handle;
CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
CHECK_VAL(io1.out.lease_response.lease_state,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
/* Disconnect after getting the lease */
talloc_free(tree1);
tree1 = NULL;
/*
* Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
* even if the original client is gone. (ZML: This seems like a bug. It
* should give some time for the client to reconnect! And why RH?)
*/
status = smb2_create(tree2, mem_ctx, &io2);
CHECK_STATUS(status, NT_STATUS_OK);
h2 = io2.out.file.handle;
CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
CHECK_VAL(io2.out.lease_response.lease_state,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE);
/* What if tree1 tries to come back and reclaim? */
if (!torture_smb2_connection(tctx, &tree1)) {
torture_warning(tctx, "couldn't reconnect, bailing\n");
ret = false;
goto done;
}
ZERO_STRUCT(io1);
io1.in.fname = fname;
io1.in.durable_handle = &h1;
io1.in.lease_request = &ls1;
status = smb2_create(tree1, mem_ctx, &io1);
CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
done:
smb2_util_close(tree2, h2);
smb2_util_unlink(tree2, fname);
return ret;
}
/*
Open, take BRL, disconnect, reconnect.
*/
bool test_durable_open_lock(struct torture_context *tctx,
struct smb2_tree *tree)
{
TALLOC_CTX *mem_ctx = talloc_new(tctx);
struct smb2_create io;
struct smb2_lease ls;
struct smb2_handle h;
struct smb2_lock lck;
struct smb2_lock_element el[2];
NTSTATUS status;
char fname[256];
bool ret = true;
uint64_t lease;
/*
* Choose a random name and random lease in case the state is left a
* little funky.
*/
lease = random();
snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
/* Clean slate */
smb2_util_unlink(tree, fname);
/* Create with lease */
ZERO_STRUCT(io);
io.in.security_flags = 0x00;
io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
io.in.create_flags = 0x00000000;
io.in.reserved = 0x00000000;
io.in.desired_access = SEC_RIGHTS_FILE_ALL;
io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
io.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
NTCREATEX_SHARE_ACCESS_WRITE |
NTCREATEX_SHARE_ACCESS_DELETE;
io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
NTCREATEX_OPTIONS_ASYNC_ALERT |
NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
0x00200000;
io.in.fname = fname;
io.in.durable_open = true;
ZERO_STRUCT(ls);
ls.lease_key.data[0] = lease;
ls.lease_key.data[1] = ~lease;
ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE;
io.in.lease_request = &ls;
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h = io.out.file.handle;
CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
CHECK_VAL(io.out.lease_response.lease_state,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
ZERO_STRUCT(lck);
ZERO_STRUCT(el);
lck.in.locks = el;
lck.in.lock_count = 0x0001;
lck.in.reserved = 0x00000000;
lck.in.file.handle = h;
el[0].offset = 0;
el[0].length = 1;
el[0].reserved = 0x00000000;
el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
status = smb2_lock(tree, &lck);
CHECK_STATUS(status, NT_STATUS_OK);
/* Disconnect/Reconnect. */
talloc_free(tree);
tree = NULL;
if (!torture_smb2_connection(tctx, &tree)) {
torture_warning(tctx, "couldn't reconnect, bailing\n");
ret = false;
goto done;
}
ZERO_STRUCT(io);
io.in.fname = fname;
io.in.durable_handle = &h;
io.in.lease_request = &ls;
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h = io.out.file.handle;
lck.in.file.handle = h;
el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
status = smb2_lock(tree, &lck);
CHECK_STATUS(status, NT_STATUS_OK);
done:
smb2_util_close(tree, h);
smb2_util_unlink(tree, fname);
return ret;
}
/*
Open, disconnect, open in another tree, reconnect.
This test actually demonstrates a minimum level of respect for the durable
open in the face of another open. As long as this test shows an inability to
reconnect after an open, the oplock/lease tests above will certainly
demonstrate an error on reconnect.
*/
bool test_durable_open_open(struct torture_context *tctx,
struct smb2_tree *tree1,
struct smb2_tree *tree2)
{
TALLOC_CTX *mem_ctx = talloc_new(tctx);
struct smb2_create io1, io2;
struct smb2_lease ls;
struct smb2_handle h1, h2;
NTSTATUS status;
char fname[256];
bool ret = true;
uint64_t lease;
/*
* Choose a random name and random lease in case the state is left a
* little funky.
*/
lease = random();
snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
/* Clean slate */
smb2_util_unlink(tree1, fname);
/* Create with lease */
ZERO_STRUCT(io1);
io1.in.security_flags = 0x00;
io1.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
io1.in.create_flags = 0x00000000;
io1.in.reserved = 0x00000000;
io1.in.desired_access = SEC_RIGHTS_FILE_ALL;
io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
io1.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
NTCREATEX_OPTIONS_ASYNC_ALERT |
NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
0x00200000;
io1.in.fname = fname;
io1.in.durable_open = true;
io2 = io1;
io2.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE;
io2.in.durable_open = false;
ZERO_STRUCT(ls);
ls.lease_key.data[0] = lease;
ls.lease_key.data[1] = ~lease;
ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE;
io1.in.lease_request = &ls;
status = smb2_create(tree1, mem_ctx, &io1);
CHECK_STATUS(status, NT_STATUS_OK);
h1 = io1.out.file.handle;
CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
CHECK_VAL(io1.out.lease_response.lease_state,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE);
/* Disconnect */
talloc_free(tree1);
tree1 = NULL;
/* Open the file in tree2 */
status = smb2_create(tree2, mem_ctx, &io2);
CHECK_STATUS(status, NT_STATUS_OK);
h2 = io2.out.file.handle;
CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
/* Reconnect */
if (!torture_smb2_connection(tctx, &tree1)) {
torture_warning(tctx, "couldn't reconnect, bailing\n");
ret = false;
goto done;
}
ZERO_STRUCT(io1);
io1.in.fname = fname;
io1.in.durable_handle = &h1;
io1.in.lease_request = &ls;
/*
* Windows7 (build 7000) will give away an open immediately if the
* original client is gone. (ZML: This seems like a bug. It should give
* some time for the client to reconnect!)
*/
status = smb2_create(tree1, mem_ctx, &io1);
CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
h1 = io1.out.file.handle;
done:
smb2_util_close(tree2, h2);
smb2_util_unlink(tree2, fname);
smb2_util_close(tree1, h1);
smb2_util_unlink(tree1, fname);
return ret;
}
struct torture_suite *torture_smb2_durable_open_init(void)
@ -295,6 +592,9 @@ struct torture_suite *torture_smb2_durable_open_init(void)
torture_suite_add_2smb2_test(suite, "FILE-POSITION",
test_durable_open_file_position);
torture_suite_add_2smb2_test(suite, "OPLOCK", test_durable_open_oplock);
torture_suite_add_2smb2_test(suite, "LEASE", test_durable_open_lease);
torture_suite_add_1smb2_test(suite, "LOCK", test_durable_open_lock);
torture_suite_add_2smb2_test(suite, "OPEN", test_durable_open_open);
suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");

View File

@ -20,16 +20,38 @@
*/
#include "includes.h"
#include "lib/events/events.h"
#include "librpc/gen_ndr/security.h"
#include "libcli/smb2/smb2.h"
#include "libcli/smb2/smb2_calls.h"
#include "torture/torture.h"
#include "torture/smb2/proto.h"
static inline uint32_t lease(const char *ls) {
uint32_t val = 0;
int i;
for (i = 0; i < strlen(ls); i++) {
switch (ls[i]) {
case 'R':
val |= SMB2_LEASE_READ;
break;
case 'H':
val |= SMB2_LEASE_HANDLE;
break;
case 'W':
val |= SMB2_LEASE_WRITE;
break;
}
}
return val;
}
#define CHECK_VAL(v, correct) do { \
if ((v) != (correct)) { \
torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
__location__, #v, (int)v, (int)correct); \
__location__, #v, (int)(v), (int)(correct)); \
ret = false; \
}} while (0)
@ -41,13 +63,14 @@
goto done; \
}} while (0)
static void smb2_lease_create(struct smb2_create *io, struct smb2_lease *ls,
bool dir, const char *name, uint64_t leasekey,
uint32_t leasestate)
static void smb2_generic_create(struct smb2_create *io, struct smb2_lease *ls,
bool dir, const char *name, uint32_t disposition,
uint32_t oplock, uint64_t leasekey,
uint32_t leasestate)
{
ZERO_STRUCT(*io);
io->in.security_flags = 0x00;
io->in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
io->in.oplock_level = oplock;
io->in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
io->in.create_flags = 0x00000000;
io->in.reserved = 0x00000000;
@ -56,7 +79,7 @@ static void smb2_lease_create(struct smb2_create *io, struct smb2_lease *ls,
io->in.share_access = NTCREATEX_SHARE_ACCESS_READ |
NTCREATEX_SHARE_ACCESS_WRITE |
NTCREATEX_SHARE_ACCESS_DELETE;
io->in.create_disposition = NTCREATEX_DISP_OPEN_IF;
io->in.create_disposition = disposition;
io->in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
NTCREATEX_OPTIONS_ASYNC_ALERT |
NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
@ -66,24 +89,37 @@ static void smb2_lease_create(struct smb2_create *io, struct smb2_lease *ls,
if (dir) {
io->in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
io->in.share_access &= ~NTCREATEX_SHARE_ACCESS_DELETE;
io->in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
io->in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
io->in.create_disposition = NTCREATEX_DISP_CREATE;
}
ZERO_STRUCT(*ls);
ls->lease_key[0] = leasekey;
ls->lease_key[1] = ~leasekey;
ls->lease_state = leasestate;
io->in.lease_request = ls;
if (ls) {
ZERO_STRUCT(*ls);
ls->lease_key.data[0] = leasekey;
ls->lease_key.data[1] = ~leasekey;
ls->lease_state = leasestate;
io->in.lease_request = ls;
}
}
static void smb2_lease_create(struct smb2_create *io, struct smb2_lease *ls,
bool dir, const char *name, uint64_t leasekey,
uint32_t leasestate)
{
smb2_generic_create(io, ls, dir, name, NTCREATEX_DISP_OPEN_IF,
SMB2_OPLOCK_LEVEL_LEASE, leasekey, leasestate);
}
static void smb2_oplock_create(struct smb2_create *io, const char *name,
uint32_t oplock)
{
smb2_generic_create(io, NULL, false, name, NTCREATEX_DISP_OPEN_IF,
oplock, 0, 0);
}
#define CHECK_CREATED(__io, __created, __attribute) \
do { \
if (__created) { \
CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_CREATED); \
} else { \
CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_EXISTED); \
} \
CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
CHECK_VAL((__io)->out.alloc_size, 0); \
CHECK_VAL((__io)->out.size, 0); \
CHECK_VAL((__io)->out.file_attr, (__attribute)); \
@ -94,13 +130,13 @@ static void smb2_lease_create(struct smb2_create *io, struct smb2_lease *ls,
do { \
if (__oplevel) { \
CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
CHECK_VAL((__io)->out.lease_response.lease_key[0], (__key)); \
CHECK_VAL((__io)->out.lease_response.lease_key[1], ~(__key)); \
CHECK_VAL((__io)->out.lease_response.lease_state, (__state)); \
CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
CHECK_VAL((__io)->out.lease_response.lease_state, lease(__state)); \
} else { \
CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
CHECK_VAL((__io)->out.lease_response.lease_key[0], 0); \
CHECK_VAL((__io)->out.lease_response.lease_key[1], 0); \
CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
} \
\
@ -112,19 +148,16 @@ static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull;
#define NRESULTS 8
static const int request_results[NRESULTS][2] = {
{ SMB2_LEASE_NONE, SMB2_LEASE_NONE },
{ SMB2_LEASE_READ, SMB2_LEASE_READ },
{ SMB2_LEASE_HANDLE, SMB2_LEASE_NONE, },
{ SMB2_LEASE_WRITE, SMB2_LEASE_NONE },
{ SMB2_LEASE_READ|SMB2_LEASE_HANDLE,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE },
{ SMB2_LEASE_READ|SMB2_LEASE_WRITE,
SMB2_LEASE_READ|SMB2_LEASE_WRITE },
{ SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE, SMB2_LEASE_NONE },
{ SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE },
#define NREQUEST_RESULTS 8
static const char *request_results[NREQUEST_RESULTS][2] = {
{ "", "" },
{ "R", "R" },
{ "H", "" },
{ "W", "" },
{ "RH", "RH" },
{ "RW", "RW" },
{ "HW", "" },
{ "RHW", "RHW" },
};
static bool test_lease_request(struct torture_context *tctx,
@ -147,53 +180,49 @@ static bool test_lease_request(struct torture_context *tctx,
smb2_util_rmdir(tree, dname);
/* Win7 is happy to grant RHW leases on files. */
smb2_lease_create(&io, &ls, false, fname, LEASE1,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h1 = io.out.file.handle;
CHECK_CREATED(&io, true, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE,
true, LEASE1);
CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, "RHW", true, LEASE1);
/* But will reject leases on directories. */
smb2_lease_create(&io, &ls, true, dname, LEASE2,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
smb2_lease_create(&io, &ls, true, dname, LEASE2, lease("RHW"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
CHECK_CREATED(&io, true, FILE_ATTRIBUTE_DIRECTORY);
CHECK_LEASE(&io, SMB2_LEASE_NONE, false, 0);
CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
CHECK_LEASE(&io, "", false, 0);
smb2_util_close(tree, io.out.file.handle);
/* Also rejects multiple files leased under the same key. */
smb2_lease_create(&io, &ls, true, fname2, LEASE1,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
smb2_lease_create(&io, &ls, true, fname2, LEASE1, lease("RHW"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
/* And grants leases on streams (with separate leasekey). */
smb2_lease_create(&io, &ls, false, sname, LEASE2,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
smb2_lease_create(&io, &ls, false, sname, LEASE2, lease("RHW"));
status = smb2_create(tree, mem_ctx, &io);
h2 = io.out.file.handle;
CHECK_STATUS(status, NT_STATUS_OK);
CHECK_CREATED(&io, true, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE,
true, LEASE2);
CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, "RHW", true, LEASE2);
smb2_util_close(tree, h2);
smb2_util_close(tree, h1);
/* Now see what combos are actually granted. */
for (i = 0; i < NRESULTS; i++) {
torture_comment(tctx, "Testing lease type %x, expecting %x\n",
request_results[i][0], request_results[i][1]);
for (i = 0; i < NREQUEST_RESULTS; i++) {
torture_comment(tctx, "Requesting lease type %s(%x),"
" expecting %s(%x)\n",
request_results[i][0], lease(request_results[i][0]),
request_results[i][1], lease(request_results[i][1]));
smb2_lease_create(&io, &ls, false, fname, LEASE1,
request_results[i][0]);
lease(request_results[i][0]));
status = smb2_create(tree, mem_ctx, &io);
h2 = io.out.file.handle;
CHECK_STATUS(status, NT_STATUS_OK);
CHECK_CREATED(&io, false, FILE_ATTRIBUTE_ARCHIVE);
CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, request_results[i][1], true, LEASE1);
smb2_util_close(tree, io.out.file.handle);
}
@ -225,48 +254,40 @@ static bool test_lease_upgrade(struct torture_context *tctx,
smb2_util_unlink(tree, fname);
/* Grab a RH lease. */
smb2_lease_create(&io, &ls, false, fname, LEASE1,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE);
smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
CHECK_CREATED(&io, true, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE, true, LEASE1);
CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, "RH", true, LEASE1);
h = io.out.file.handle;
/* Upgrades (sidegrades?) to RW leave us with an RH. */
smb2_lease_create(&io, &ls, false, fname, LEASE1,
SMB2_LEASE_READ|SMB2_LEASE_WRITE);
smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RW"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
CHECK_CREATED(&io, false, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE, true, LEASE1);
CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, "RH", true, LEASE1);
hnew = io.out.file.handle;
smb2_util_close(tree, hnew);
/* Upgrade to RHW lease. */
smb2_lease_create(&io, &ls, false, fname, LEASE1,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
CHECK_CREATED(&io, false, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE,
true, LEASE1);
CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, "RHW", true, LEASE1);
hnew = io.out.file.handle;
smb2_util_close(tree, h);
h = hnew;
/* Attempt to downgrade - original lease state is maintained. */
smb2_lease_create(&io, &ls, false, fname, LEASE1,
SMB2_LEASE_READ|SMB2_LEASE_HANDLE);
smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
CHECK_CREATED(&io, false, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE,
true, LEASE1);
CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, "RHW", true, LEASE1);
hnew = io.out.file.handle;
smb2_util_close(tree, hnew);
@ -282,6 +303,534 @@ static bool test_lease_upgrade(struct torture_context *tctx,
return ret;
}
#define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key) \
do { \
CHECK_VAL((__lb)->new_lease_state, lease(__state)); \
CHECK_VAL((__lb)->current_lease.lease_state, lease(__oldstate)); \
CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \
CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \
} while(0)
#define CHECK_LEASE_BREAK_ACK(__lba, __state, __key) \
do { \
CHECK_VAL((__lba)->out.reserved, 0); \
CHECK_VAL((__lba)->out.lease.lease_key.data[0], (__key)); \
CHECK_VAL((__lba)->out.lease.lease_key.data[1], ~(__key)); \
CHECK_VAL((__lba)->out.lease.lease_state, lease(__state)); \
CHECK_VAL((__lba)->out.lease.lease_flags, 0); \
CHECK_VAL((__lba)->out.lease.lease_duration, 0); \
} while(0)
static struct {
struct smb2_lease_break lease_break;
struct smb2_lease_break_ack lease_break_ack;
int count;
int failures;
struct smb2_handle oplock_handle;
int held_oplock_level;
int oplock_level;
int oplock_count;
int oplock_failures;
} break_info;
#define CHECK_BREAK_INFO(__oldstate, __state, __key) \
do { \
CHECK_VAL(break_info.failures, 0); \
CHECK_VAL(break_info.count, 1); \
CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
(__state), (__key)); \
if (break_info.lease_break.break_flags & \
SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { \
CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
(__state), (__key)); \
} \
} while(0)
static void torture_lease_break_callback(struct smb2_request *req)
{
NTSTATUS status;
status = smb2_lease_break_ack_recv(req, &break_info.lease_break_ack);
if (!NT_STATUS_IS_OK(status))
break_info.failures++;
return;
}
/* a lease break request handler */
static bool torture_lease_handler(struct smb2_transport *transport,
const struct smb2_lease_break *lb,
void *private_data)
{
struct smb2_tree *tree = private_data;
struct smb2_lease_break_ack io;
struct smb2_request *req;
break_info.lease_break = *lb;
break_info.count++;
if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
ZERO_STRUCT(io);
io.in.lease.lease_key = lb->current_lease.lease_key;
io.in.lease.lease_state = lb->new_lease_state;
req = smb2_lease_break_ack_send(tree, &io);
req->async.fn = torture_lease_break_callback;
req->async.private_data = NULL;
}
return true;
}
/*
break_results should be read as "held lease, new lease, hold broken to, new
grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
tries for RW, key1 will be broken to RH (in this case, not broken at all)
and key2 will be granted R.
Note: break_results only includes things that Win7 will actually grant (see
request_results above).
*/
#define NBREAK_RESULTS 16
static const char *break_results[NBREAK_RESULTS][4] = {
{"R", "R", "R", "R"},
{"R", "RH", "R", "RH"},
{"R", "RW", "R", "R"},
{"R", "RHW", "R", "RH"},
{"RH", "R", "RH", "R"},
{"RH", "RH", "RH", "RH"},
{"RH", "RW", "RH", "R"},
{"RH", "RHW", "RH", "RH"},
{"RW", "R", "R", "R"},
{"RW", "RH", "R", "RH"},
{"RW", "RW", "R", "R"},
{"RW", "RHW", "R", "RH"},
{"RHW", "R", "RH", "R"},
{"RHW", "RH", "RH", "RH"},
{"RHW", "RW", "RH", "R"},
{"RHW", "RHW", "RH", "RH"},
};
static bool test_lease_break(struct torture_context *tctx,
struct smb2_tree *tree)
{
TALLOC_CTX *mem_ctx = talloc_new(tctx);
struct smb2_create io;
struct smb2_lease ls;
struct smb2_handle h, h2, h3;
NTSTATUS status;
const char *fname = "lease.dat";
bool ret = true;
int i;
tree->session->transport->lease.handler = torture_lease_handler;
tree->session->transport->lease.private_data = tree;
smb2_util_unlink(tree, fname);
for (i = 0; i < NBREAK_RESULTS; i++) {
const char *held = break_results[i][0];
const char *contend = break_results[i][1];
const char *brokento = break_results[i][2];
const char *granted = break_results[i][3];
torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
"expecting break to %s(%x) and grant of %s(%x)\n",
held, lease(held), contend, lease(contend),
brokento, lease(brokento), granted, lease(granted));
ZERO_STRUCT(break_info);
/* Grab lease. */
smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h = io.out.file.handle;
CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, held, true, LEASE1);
/* Possibly contend lease. */
smb2_lease_create(&io, &ls, false, fname, LEASE2, lease(contend));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h2 = io.out.file.handle;
CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, granted, true, LEASE2);
if (lease(held) != lease(brokento)) {
CHECK_BREAK_INFO(held, brokento, LEASE1);
} else {
CHECK_VAL(break_info.count, 0);
CHECK_VAL(break_info.failures, 0);
}
ZERO_STRUCT(break_info);
/*
Now verify that an attempt to upgrade LEASE1 results in no
break and no change in LEASE1.
*/
smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h3 = io.out.file.handle;
CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, brokento, true, LEASE1);
CHECK_VAL(break_info.count, 0);
CHECK_VAL(break_info.failures, 0);
smb2_util_close(tree, h);
smb2_util_close(tree, h2);
smb2_util_close(tree, h3);
status = smb2_util_unlink(tree, fname);
CHECK_STATUS(status, NT_STATUS_OK);
}
done:
smb2_util_close(tree, h);
smb2_util_close(tree, h2);
smb2_util_unlink(tree, fname);
talloc_free(mem_ctx);
return ret;
}
static void torture_oplock_break_callback(struct smb2_request *req)
{
NTSTATUS status;
struct smb2_break br;
ZERO_STRUCT(br);
status = smb2_break_recv(req, &br);
if (!NT_STATUS_IS_OK(status))
break_info.oplock_failures++;
return;
}
/* a oplock break request handler */
static bool torture_oplock_handler(struct smb2_transport *transport,
const struct smb2_handle *handle,
uint8_t level, void *private_data)
{
struct smb2_tree *tree = private_data;
struct smb2_request *req;
struct smb2_break br;
break_info.oplock_handle = *handle;
break_info.oplock_level = level;
break_info.oplock_count++;
ZERO_STRUCT(br);
br.in.file.handle = *handle;
br.in.oplock_level = level;
if (break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
req = smb2_break_send(tree, &br);
req->async.fn = torture_oplock_break_callback;
req->async.private_data = NULL;
}
break_info.held_oplock_level = level;
return true;
}
static inline uint32_t oplock(const char *op) {
uint32_t val = SMB2_OPLOCK_LEVEL_NONE;
int i;
for (i = 0; i < strlen(op); i++) {
switch (op[i]) {
case 's':
return SMB2_OPLOCK_LEVEL_II;
case 'x':
return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
case 'b':
return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
default:
continue;
}
}
return val;
}
#define NOPLOCK_RESULTS 12
static const char *oplock_results[NOPLOCK_RESULTS][4] = {
{"R", "s", "R", "s"},
{"R", "x", "R", "s"},
{"R", "b", "R", "s"},
{"RH", "s", "RH", ""},
{"RH", "x", "RH", ""},
{"RH", "b", "RH", ""},
{"RW", "s", "R", "s"},
{"RW", "x", "R", "s"},
{"RW", "b", "R", "s"},
{"RHW", "s", "RH", ""},
{"RHW", "x", "RH", ""},
{"RHW", "b", "RH", ""},
};
static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
{"s", "R", "s", "R"},
{"s", "RH", "s", "R"},
{"s", "RW", "s", "R"},
{"s", "RHW", "s", "R"},
{"x", "R", "s", "R"},
{"x", "RH", "s", "R"},
{"x", "RW", "s", "R"},
{"x", "RHW", "s", "R"},
{"b", "R", "s", "R"},
{"b", "RH", "s", "R"},
{"b", "RW", "s", "R"},
{"b", "RHW", "s", "R"},
};
static bool test_lease_oplock(struct torture_context *tctx,
struct smb2_tree *tree)
{
TALLOC_CTX *mem_ctx = talloc_new(tctx);
struct smb2_create io;
struct smb2_lease ls;
struct smb2_handle h, h2;
NTSTATUS status;
const char *fname = "lease.dat";
bool ret = true;
int i;
tree->session->transport->lease.handler = torture_lease_handler;
tree->session->transport->lease.private_data = tree;
tree->session->transport->oplock.handler = torture_oplock_handler;
tree->session->transport->oplock.private_data = tree;
smb2_util_unlink(tree, fname);
for (i = 0; i < NOPLOCK_RESULTS; i++) {
const char *held = oplock_results[i][0];
const char *contend = oplock_results[i][1];
const char *brokento = oplock_results[i][2];
const char *granted = oplock_results[i][3];
torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
"expecting break to %s(%x) and grant of %s(%x)\n",
held, lease(held), contend, oplock(contend),
brokento, lease(brokento), granted, oplock(granted));
ZERO_STRUCT(break_info);
/* Grab lease. */
smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h = io.out.file.handle;
CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, held, true, LEASE1);
/* Does an oplock contend the lease? */
smb2_oplock_create(&io, fname, oplock(contend));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h2 = io.out.file.handle;
CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io.out.oplock_level, oplock(granted));
break_info.held_oplock_level = io.out.oplock_level;
if (lease(held) != lease(brokento)) {
CHECK_BREAK_INFO(held, brokento, LEASE1);
} else {
CHECK_VAL(break_info.count, 0);
CHECK_VAL(break_info.failures, 0);
}
smb2_util_close(tree, h);
smb2_util_close(tree, h2);
status = smb2_util_unlink(tree, fname);
CHECK_STATUS(status, NT_STATUS_OK);
}
for (i = 0; i < NOPLOCK_RESULTS; i++) {
const char *held = oplock_results_2[i][0];
const char *contend = oplock_results_2[i][1];
const char *brokento = oplock_results_2[i][2];
const char *granted = oplock_results_2[i][3];
torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
"expecting break to %s(%x) and grant of %s(%x)\n",
held, oplock(held), contend, lease(contend),
brokento, oplock(brokento), granted, lease(granted));
ZERO_STRUCT(break_info);
/* Grab an oplock. */
smb2_oplock_create(&io, fname, oplock(held));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h = io.out.file.handle;
CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io.out.oplock_level, oplock(held));
break_info.held_oplock_level = io.out.oplock_level;
/* Grab lease. */
smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(contend));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h2 = io.out.file.handle;
CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, granted, true, LEASE1);
if (oplock(held) != oplock(brokento)) {
CHECK_VAL(break_info.oplock_count, 1);
CHECK_VAL(break_info.oplock_failures, 0);
CHECK_VAL(break_info.oplock_level, oplock(brokento));
break_info.held_oplock_level = break_info.oplock_level;
} else {
CHECK_VAL(break_info.oplock_count, 0);
CHECK_VAL(break_info.oplock_failures, 0);
}
smb2_util_close(tree, h);
smb2_util_close(tree, h2);
status = smb2_util_unlink(tree, fname);
CHECK_STATUS(status, NT_STATUS_OK);
}
done:
smb2_util_close(tree, h);
smb2_util_close(tree, h2);
smb2_util_unlink(tree, fname);
talloc_free(mem_ctx);
return ret;
}
static bool test_lease_multibreak(struct torture_context *tctx,
struct smb2_tree *tree)
{
TALLOC_CTX *mem_ctx = talloc_new(tctx);
struct smb2_create io;
struct smb2_lease ls;
struct smb2_handle h, h2, h3;
struct smb2_write w;
NTSTATUS status;
const char *fname = "lease.dat";
bool ret = true;
tree->session->transport->lease.handler = torture_lease_handler;
tree->session->transport->lease.private_data = tree;
tree->session->transport->oplock.handler = torture_oplock_handler;
tree->session->transport->oplock.private_data = tree;
smb2_util_unlink(tree, fname);
ZERO_STRUCT(break_info);
/* Grab lease, upgrade to RHW .. */
smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h = io.out.file.handle;
CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, "RH", true, LEASE1);
smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h2 = io.out.file.handle;
CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, "RHW", true, LEASE1);
/* Contend with LEASE2. */
smb2_lease_create(&io, &ls, false, fname, LEASE2, lease("RHW"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h3 = io.out.file.handle;
CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, "RH", true, LEASE2);
/* Verify that we were only sent one break. */
CHECK_BREAK_INFO("RHW", "RH", LEASE1);
/* Drop LEASE1 / LEASE2 */
status = smb2_util_close(tree, h);
CHECK_STATUS(status, NT_STATUS_OK);
status = smb2_util_close(tree, h2);
CHECK_STATUS(status, NT_STATUS_OK);
status = smb2_util_close(tree, h3);
CHECK_STATUS(status, NT_STATUS_OK);
ZERO_STRUCT(break_info);
/* Grab an R lease. */
smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("R"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h = io.out.file.handle;
CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_LEASE(&io, "R", true, LEASE1);
/* Grab a level-II oplock. */
smb2_oplock_create(&io, fname, oplock("s"));
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h2 = io.out.file.handle;
CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io.out.oplock_level, oplock("s"));
break_info.held_oplock_level = io.out.oplock_level;
/* Verify no breaks. */
CHECK_VAL(break_info.count, 0);
CHECK_VAL(break_info.failures, 0);
/* Open for truncate, force a break. */
smb2_generic_create(&io, NULL, false, fname,
NTCREATEX_DISP_OVERWRITE_IF, oplock(""), 0, 0);
status = smb2_create(tree, mem_ctx, &io);
CHECK_STATUS(status, NT_STATUS_OK);
h3 = io.out.file.handle;
CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
CHECK_VAL(io.out.oplock_level, oplock(""));
break_info.held_oplock_level = io.out.oplock_level;
/* Sleep, use a write to clear the recv queue. */
msleep(250);
ZERO_STRUCT(w);
w.in.file.handle = h3;
w.in.offset = 0;
w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
status = smb2_write(tree, &w);
CHECK_STATUS(status, NT_STATUS_OK);
/* Verify one oplock break, one lease break. */
CHECK_VAL(break_info.oplock_count, 1);
CHECK_VAL(break_info.oplock_failures, 0);
CHECK_VAL(break_info.oplock_level, oplock(""));
CHECK_BREAK_INFO("R", "", LEASE1);
done:
smb2_util_close(tree, h);
smb2_util_close(tree, h2);
smb2_util_close(tree, h3);
smb2_util_unlink(tree, fname);
talloc_free(mem_ctx);
return ret;
}
struct torture_suite *torture_smb2_lease_init(void)
{
struct torture_suite *suite =
@ -289,6 +838,9 @@ struct torture_suite *torture_smb2_lease_init(void)
torture_suite_add_1smb2_test(suite, "REQUEST", test_lease_request);
torture_suite_add_1smb2_test(suite, "UPGRADE", test_lease_upgrade);
torture_suite_add_1smb2_test(suite, "BREAK", test_lease_break);
torture_suite_add_1smb2_test(suite, "OPLOCK", test_lease_oplock);
torture_suite_add_1smb2_test(suite, "MULTIBREAK", test_lease_multibreak);
suite->description = talloc_strdup(suite, "SMB2-LEASE tests");