summaryrefslogtreecommitdiff
path: root/usr/src/cmd/smbsrv
diff options
context:
space:
mode:
authorGordon Ross <gwr@nexenta.com>2017-07-21 16:37:02 -0400
committerGordon Ross <gwr@nexenta.com>2019-06-08 20:48:57 -0400
commit94047d49916b669576decf2f622a1ee718646882 (patch)
tree6b446f44e97da3deccef4504c5f8bd82f14a35c8 /usr/src/cmd/smbsrv
parent148d1a4158dc830f7b293a2ceb62ee54c2ebd72f (diff)
downloadillumos-joyent-94047d49916b669576decf2f622a1ee718646882.tar.gz
11016 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com> Reviewed by: Evan Layton <evan.layton@nexenta.com> Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com> Approved by: Garrett D'Amore <garrett@damore.org>
Diffstat (limited to 'usr/src/cmd/smbsrv')
-rw-r--r--usr/src/cmd/smbsrv/Makefile4
-rw-r--r--usr/src/cmd/smbsrv/testoplock/.dbxrc23
-rw-r--r--usr/src/cmd/smbsrv/testoplock/Makefile121
-rwxr-xr-xusr/src/cmd/smbsrv/testoplock/Run-cmd.sh32
-rwxr-xr-xusr/src/cmd/smbsrv/testoplock/Run-tests.sh47
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case01.ref29
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case01.txt10
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case02.ref29
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case02.txt10
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case03.ref20
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case03.txt7
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case04.ref20
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case04.txt7
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case05.ref65
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case05.txt18
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case06.ref56
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case06.txt24
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case07.ref20
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case07.txt8
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case08.ref36
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case08.txt13
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case09.ref27
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case09.txt10
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case10.ref31
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case10.txt12
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case11.ref30
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case11.txt11
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case12.ref54
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case12.txt28
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case13.ref56
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case13.txt25
-rw-r--r--usr/src/cmd/smbsrv/testoplock/smbsrv/smb_kproto.h111
-rw-r--r--usr/src/cmd/smbsrv/testoplock/smbsrv/smb_ktypes.h227
-rw-r--r--usr/src/cmd/smbsrv/testoplock/tol_all.d106
-rw-r--r--usr/src/cmd/smbsrv/testoplock/tol_main.c641
-rw-r--r--usr/src/cmd/smbsrv/testoplock/tol_misc.c179
36 files changed, 2145 insertions, 2 deletions
diff --git a/usr/src/cmd/smbsrv/Makefile b/usr/src/cmd/smbsrv/Makefile
index 193ce84c88..8e7699c252 100644
--- a/usr/src/cmd/smbsrv/Makefile
+++ b/usr/src/cmd/smbsrv/Makefile
@@ -22,11 +22,11 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2017 Nexenta Systems, Inc. All rights reserved.
#
SUBDIRS = smbadm smbd smbstat dtrace fksmbd bind-helper \
- test-msgbuf
+ test-msgbuf testoplock
MSGSUBDIRS = smbadm smbstat
include ../Makefile.cmd
diff --git a/usr/src/cmd/smbsrv/testoplock/.dbxrc b/usr/src/cmd/smbsrv/testoplock/.dbxrc
new file mode 100644
index 0000000000..92f30a38f7
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/.dbxrc
@@ -0,0 +1,23 @@
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017 Nexenta Systems, Inc. All rights reserved.
+#
+
+set -o emacs
+
+# testoplock is always 32-bit
+setenv LD_LIBRARY_PATH ${ROOT}/usr/lib/smbsrv:${ROOT}/usr/lib:${ROOT}/lib
+
+echo 'Do one of: attach ${PID}'
+echo 'or: debug ${ROOT}/usr/lib/smbsrv/testoplock'
diff --git a/usr/src/cmd/smbsrv/testoplock/Makefile b/usr/src/cmd/smbsrv/testoplock/Makefile
new file mode 100644
index 0000000000..12fcbd0ac9
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/Makefile
@@ -0,0 +1,121 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+#
+
+
+PROG= testoplock
+
+OBJS_LOCAL= tol_main.o tol_misc.o
+OBJS_SMBSRV= smb_cmn_oplock.o
+OBJS_LIBSMB= smb_status_tbl.o
+
+OBJS= ${OBJS_LOCAL} ${OBJS_SMBSRV} ${OBJS_LIBSMB}
+
+SMBSRV_SRCDIR=../../../uts/common/fs/smbsrv
+SRCS= ${OBJS_LOCAL:.o=.c} \
+ ${OBJS_SMBSRV:%.o=${SMBSRV_SRCDIR}/%.c}
+
+include ../../Makefile.cmd
+include ../../Makefile.ctf
+
+# Note: need our sys includes _before_ ENVCPPFLAGS, proto etc.
+CPPFLAGS.first += -I.
+CPPFLAGS.first += -I../../../lib/libfakekernel/common
+CPPFLAGS.first += -I../../../lib/smbsrv/libfksmbsrv/common
+
+INCS += -I../../../uts/common
+
+CSTD= $(CSTD_GNU99)
+
+CFLAGS += $(CCVERBOSE)
+CFLAGS64 += $(CCVERBOSE)
+
+CPPFLAGS.master=$(DTEXTDOM) $(DTS_ERRNO)
+
+# CPPFLAGS is deliberatly set with a "=" and not a "+="...
+CPPFLAGS= $(CPPFLAGS.first) $(CPPFLAGS.master)
+
+CPPFLAGS += -D_REENTRANT
+CPPFLAGS += -DTESTJIG
+CPPFLAGS += -Dsyslog=smb_syslog
+CPPFLAGS += -D_LARGEFILE64_SOURCE=1
+# Always debug here
+CPPFLAGS += -DDEBUG
+CPPFLAGS += $(INCS)
+
+LDFLAGS += $(ZNOLAZYLOAD)
+LDFLAGS += '-R$$ORIGIN/..'
+LDLIBS += -lfakekernel -lcmdutils
+
+LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2
+LINTFLAGS += -xerroff=E_NAME_USED_NOT_DEF2
+LINTFLAGS += -xerroff=E_INCONS_ARG_DECL2
+LINTFLAGS += -xerroff=E_INCONS_VAL_TYPE_DECL2
+
+ROOTSMBDDIR = $(ROOTLIB)/smbsrv
+ROOTSMBDFILE = $(PROG:%=$(ROOTSMBDDIR)/%)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+clean:
+ -$(RM) $(OBJS)
+
+lint: # lint_SRCS
+
+include ../../Makefile.targ
+
+install: all $(ROOTSMBDFILE)
+
+
+tol_main.o : tol_main.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D_KMEMUSER -c tol_main.c
+ $(POST_PROCESS_O)
+
+tol_misc.o : tol_misc.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \
+ -I../../../uts/common/smbsrv \
+ -I../../../common/smbsrv -c tol_misc.c
+ $(POST_PROCESS_O)
+
+# OBJS_SMBSRV
+%.o: ../../../uts/common/fs/smbsrv/%.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \
+ -I../../../uts/common/smbsrv \
+ -I../../../common/smbsrv -c $<
+ $(POST_PROCESS_O)
+
+# OBJS_LIBSMB
+%.o: ../../../lib/smbsrv/libsmb/common/%.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $<
+ $(POST_PROCESS_O)
+
+
+$(ROOTSMBDDIR)/%: %
+ $(INS.file)
diff --git a/usr/src/cmd/smbsrv/testoplock/Run-cmd.sh b/usr/src/cmd/smbsrv/testoplock/Run-cmd.sh
new file mode 100755
index 0000000000..fa34046936
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/Run-cmd.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017 Nexenta Systems, Inc. All rights reserved.
+#
+
+# Helper program to run fksmbd (user-space smbd for debugging)
+# using binaries from the proto area.
+
+[ -n "$ROOT" ] || {
+ echo "Need a bldenv to set ROOT=..."
+ exit 1;
+}
+
+# OK, setup env. to run it.
+
+LD_LIBRARY_PATH=$ROOT/usr/lib:$ROOT/lib
+export LD_LIBRARY_PATH
+
+# run with the passed options
+exec $ROOT/usr/lib/smbsrv/testoplock "$@"
diff --git a/usr/src/cmd/smbsrv/testoplock/Run-tests.sh b/usr/src/cmd/smbsrv/testoplock/Run-tests.sh
new file mode 100755
index 0000000000..aa5174095b
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/Run-tests.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017 Nexenta Systems, Inc. All rights reserved.
+#
+
+# Helper program to run fksmbd (user-space smbd for debugging)
+# using binaries from the proto area.
+
+[ -n "$ROOT" ] || {
+ echo "Need a bldenv to set ROOT=..."
+ exit 1;
+}
+
+# OK, setup env. to run it.
+
+LD_LIBRARY_PATH=$ROOT/usr/lib:$ROOT/lib
+export LD_LIBRARY_PATH
+
+TOL=$ROOT/usr/lib/smbsrv/testoplock
+
+TESTS=${@:-case??.txt}
+
+# run the test cases
+for t in $TESTS
+do
+ name=${t%.txt}
+ $TOL < $name.txt > $name.tmp
+ if diff -u $name.tmp $name.ref >/dev/null 2>&1 ; then
+ echo "$name PASS"
+ rm $name.tmp
+ else
+ echo "$name FAIL"
+ diff -u $name.tmp $name.ref
+ fi
+done
diff --git a/usr/src/cmd/smbsrv/testoplock/case01.ref b/usr/src/cmd/smbsrv/testoplock/case01.ref
new file mode 100644
index 0000000000..6327c46b7e
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case01.ref
@@ -0,0 +1,29 @@
+open 1
+ open 1 OK
+req 1
+ req oplock fid=1 ret oplock=0x400 status=0x0 (SUCCESS)
+show
+ ol_state=0x410 ( BATCH_OPLOCK EXCLUSIVE )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease= OgState=0x400 Brk=0x0 Excl=Y onlist:
+brk-open 2
+*smb_oplock_ind_break fid=1 NewLevel=0x100, AckReq=1, ComplStatus=0x0 (SUCCESS)
+ brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
+show
+ ol_state=0x100410 ( BREAK_TO_TWO BATCH_OPLOCK EXCLUSIVE )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease= OgState=0x400 Brk=0x100000 Excl=Y onlist:
+ack 1
+ ack: break fid=1, newstate=0x100, status=0x0 (SUCCESS)
+open 2
+ open 2 OK
+req 2 0x100
+ req oplock fid=2 ret oplock=0x100 status=0x0 (SUCCESS)
+show
+ ol_state=0x100 ( LEVEL_TWO_OPLOCK )
+ Excl=n cnt_II=2 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
+ fid=2 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
diff --git a/usr/src/cmd/smbsrv/testoplock/case01.txt b/usr/src/cmd/smbsrv/testoplock/case01.txt
new file mode 100644
index 0000000000..58eaa7f4b1
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case01.txt
@@ -0,0 +1,10 @@
+# Input for testoplock, case 01
+open 1
+req 1
+show
+brk-open 2
+show
+ack 1
+open 2
+req 2 0x100
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/case02.ref b/usr/src/cmd/smbsrv/testoplock/case02.ref
new file mode 100644
index 0000000000..73488cbf27
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case02.ref
@@ -0,0 +1,29 @@
+open 1
+ open 1 OK
+req 1 0x807
+ req oplock fid=1 ret oplock=0x807 status=0x0 (SUCCESS)
+show
+ ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease= OgState=0x807 Brk=0x0 Excl=Y onlist:
+brk-open 2
+*smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS)
+ brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
+show
+ ol_state=0x30017 ( BREAK_TO_HANDLE_CACHING BREAK_TO_READ_CACHING EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease= OgState=0x807 Brk=0x30000 Excl=Y onlist:
+ack 1
+ ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS)
+open 2
+ open 2 OK
+req 2 0x803
+ req oplock fid=2 ret oplock=0x803 status=0x0 (SUCCESS)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=2 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease= OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=2 Lease= OgState=0x803 Brk=0x0 Excl=N onlist: RH
diff --git a/usr/src/cmd/smbsrv/testoplock/case02.txt b/usr/src/cmd/smbsrv/testoplock/case02.txt
new file mode 100644
index 0000000000..996d41e75f
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case02.txt
@@ -0,0 +1,10 @@
+# Input for testoplock, case 02
+open 1
+req 1 0x807
+show
+brk-open 2
+show
+ack 1
+open 2
+req 2 0x803
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/case03.ref b/usr/src/cmd/smbsrv/testoplock/case03.ref
new file mode 100644
index 0000000000..cdc7fe0089
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case03.ref
@@ -0,0 +1,20 @@
+open 1 3
+ open 1 OK
+req 1 0x803
+ req oplock fid=1 ret oplock=0x803 status=0x0 (SUCCESS)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease=3 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+open 2 3
+ open 2 OK
+req 2 0x803
+*smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=0, ComplStatus=0x215 (OPLOCK_SWITCHED_TO_NEW_HANDLE)
+ req oplock fid=2 ret oplock=0x803 status=0x0 (SUCCESS)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=3 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=2 Lease=3 OgState=0x803 Brk=0x0 Excl=N onlist: RH
diff --git a/usr/src/cmd/smbsrv/testoplock/case03.txt b/usr/src/cmd/smbsrv/testoplock/case03.txt
new file mode 100644
index 0000000000..096936e0c7
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case03.txt
@@ -0,0 +1,7 @@
+# Input for testoplock, case 03
+open 1 3
+req 1 0x803
+show
+open 2 3
+req 2 0x803
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/case04.ref b/usr/src/cmd/smbsrv/testoplock/case04.ref
new file mode 100644
index 0000000000..32df3767a9
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case04.ref
@@ -0,0 +1,20 @@
+open 1 3
+ open 1 OK
+req 1 0x801
+ req oplock fid=1 ret oplock=0x801 status=0x0 (SUCCESS)
+show
+ ol_state=0x1 ( READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease=3 OgState=0x801 Brk=0x0 Excl=N onlist: R
+open 2 3
+ open 2 OK
+req 2 0x801
+*smb_oplock_ind_break fid=1 NewLevel=0x1, AckReq=0, ComplStatus=0x215 (OPLOCK_SWITCHED_TO_NEW_HANDLE)
+ req oplock fid=2 ret oplock=0x801 status=0x0 (SUCCESS)
+show
+ ol_state=0x1 ( READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=3 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=2 Lease=3 OgState=0x801 Brk=0x0 Excl=N onlist: R
diff --git a/usr/src/cmd/smbsrv/testoplock/case04.txt b/usr/src/cmd/smbsrv/testoplock/case04.txt
new file mode 100644
index 0000000000..c8316ea2d1
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case04.txt
@@ -0,0 +1,7 @@
+# Input for testoplock, case 04
+open 1 3
+req 1 0x801
+show
+open 2 3
+req 2 0x801
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/case05.ref b/usr/src/cmd/smbsrv/testoplock/case05.ref
new file mode 100644
index 0000000000..639bf0023b
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case05.ref
@@ -0,0 +1,65 @@
+open 2 4
+ open 2 OK
+req 2 0x803
+ req oplock fid=2 ret oplock=0x803 status=0x0 (SUCCESS)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=2 Lease=4 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+open 3 4
+ open 3 OK
+req 3 0x803
+*smb_oplock_ind_break fid=2 NewLevel=0x3, AckReq=0, ComplStatus=0x215 (OPLOCK_SWITCHED_TO_NEW_HANDLE)
+ req oplock fid=3 ret oplock=0x803 status=0x0 (SUCCESS)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=2 Lease=4 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=3 Lease=4 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+open 1
+ open 1 OK
+brk-write 1
+*smb_oplock_ind_break fid=3 NewLevel=0x0, AckReq=1, ComplStatus=0x0 (SUCCESS)
+ brk-write 1 ret status=0x0 (SUCCESS)
+show
+ ol_state=0x80003 ( BREAK_TO_NO_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=1
+ ofile_cnt=3
+ fid=2 Lease=4 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=3 Lease=4 OgState=0x803 Brk=0x80000 Excl=N onlist: RHBQ(to none)
+ fid=1 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ack 3
+ ack: break fid=3, newstate=0x800, status=0x0 (SUCCESS)
+show
+ ol_state=0x10000000 ( NO_OPLOCK )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=3
+ fid=2 Lease=4 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=3 Lease=4 OgState=0x800 Brk=0x0 Excl=N onlist:
+ fid=1 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+open 4 4
+ open 4 OK
+req 4 0x803
+ req oplock fid=4 ret oplock=0x803 status=0x0 (SUCCESS)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=4
+ fid=2 Lease=4 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=3 Lease=4 OgState=0x800 Brk=0x0 Excl=N onlist:
+ fid=1 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=4 Lease=4 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+close 4
+*smb_oplock_ind_break fid=4 NewLevel=0x0, AckReq=0, ComplStatus=0x216 (OPLOCK_HANDLE_CLOSED)
+ close OK
+req 3 0x803
+ req oplock fid=3 ret oplock=0x803 status=0x0 (SUCCESS)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=3
+ fid=2 Lease=4 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=3 Lease=4 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=1 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
diff --git a/usr/src/cmd/smbsrv/testoplock/case05.txt b/usr/src/cmd/smbsrv/testoplock/case05.txt
new file mode 100644
index 0000000000..f22f67abea
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case05.txt
@@ -0,0 +1,18 @@
+# Input for testoplock, case 05
+open 2 4
+req 2 0x803
+show
+open 3 4
+req 3 0x803
+show
+open 1
+brk-write 1
+show
+ack 3
+show
+open 4 4
+req 4 0x803
+show
+close 4
+req 3 0x803
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/case06.ref b/usr/src/cmd/smbsrv/testoplock/case06.ref
new file mode 100644
index 0000000000..daa69c3dd1
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case06.ref
@@ -0,0 +1,56 @@
+open 1 1
+ open 1 OK
+req 1 0x801
+ req oplock fid=1 ret oplock=0x801 status=0x0 (SUCCESS)
+open 2 2
+ open 2 OK
+req 2 0x801
+ req oplock fid=2 ret oplock=0x801 status=0x0 (SUCCESS)
+show
+ ol_state=0x1 ( READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=2 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=2 Lease=2 OgState=0x801 Brk=0x0 Excl=N onlist: R
+brk-write 1
+*smb_oplock_ind_break fid=2 NewLevel=0x0, AckReq=0, ComplStatus=0x0 (SUCCESS)
+ brk-write 1 ret status=0x0 (SUCCESS)
+show
+ ol_state=0x1 ( READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=2 Lease=2 OgState=0x800 Brk=0x0 Excl=N onlist:
+open 3 2
+ open 3 OK
+req 3 0x801
+ req oplock fid=3 ret oplock=0x801 status=0x0 (SUCCESS)
+close 3
+*smb_oplock_ind_break fid=3 NewLevel=0x0, AckReq=0, ComplStatus=0x216 (OPLOCK_HANDLE_CLOSED)
+ close OK
+req 2 0x801
+ req oplock fid=2 ret oplock=0x801 status=0x0 (SUCCESS)
+show
+ ol_state=0x1 ( READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=2 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=2 Lease=2 OgState=0x801 Brk=0x0 Excl=N onlist: R
+brk-write 2
+*smb_oplock_ind_break fid=1 NewLevel=0x0, AckReq=0, ComplStatus=0x0 (SUCCESS)
+ brk-write 2 ret status=0x0 (SUCCESS)
+show
+ ol_state=0x1 ( READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x800 Brk=0x0 Excl=N onlist:
+ fid=2 Lease=2 OgState=0x801 Brk=0x0 Excl=N onlist: R
+brk-write 1
+*smb_oplock_ind_break fid=2 NewLevel=0x0, AckReq=0, ComplStatus=0x0 (SUCCESS)
+ brk-write 1 ret status=0x0 (SUCCESS)
+show
+ ol_state=0x10000000 ( NO_OPLOCK )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x800 Brk=0x0 Excl=N onlist:
+ fid=2 Lease=2 OgState=0x800 Brk=0x0 Excl=N onlist:
diff --git a/usr/src/cmd/smbsrv/testoplock/case06.txt b/usr/src/cmd/smbsrv/testoplock/case06.txt
new file mode 100644
index 0000000000..c4af8377d0
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case06.txt
@@ -0,0 +1,24 @@
+# Input for testoplock, case 06
+# Modeled after smbtorture smb2.lease.nobreakself
+open 1 1
+req 1 0x801
+open 2 2
+req 2 0x801
+# both 1,2 should have R
+show
+# write 1 should leave 1:R 2:none
+brk-write 1
+show
+# upgrade 2 back to R
+open 3 2
+req 3 0x801
+close 3
+# ind_break will "move" the lease to h2 (1:R 2:R)
+req 2 0x801
+show
+# write 2 should leave 1:none 2:R
+brk-write 2
+show
+# write 1 should leave 1:none 2:none
+brk-write 1
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/case07.ref b/usr/src/cmd/smbsrv/testoplock/case07.ref
new file mode 100644
index 0000000000..11b1fa415d
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case07.ref
@@ -0,0 +1,20 @@
+open 1 3
+ open 1 OK
+req 1 0x803
+ req oplock fid=1 ret oplock=0x803 status=0x0 (SUCCESS)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease=3 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+open 2 3
+ open 2 OK
+req 2 0x807
+*smb_oplock_ind_break fid=1 NewLevel=0x7, AckReq=0, ComplStatus=0x215 (OPLOCK_SWITCHED_TO_NEW_HANDLE)
+ req oplock fid=2 ret oplock=0x807 status=0x0 (SUCCESS)
+show
+ ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=2) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=3 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=2 Lease=3 OgState=0x807 Brk=0x0 Excl=Y onlist:
diff --git a/usr/src/cmd/smbsrv/testoplock/case07.txt b/usr/src/cmd/smbsrv/testoplock/case07.txt
new file mode 100644
index 0000000000..56d90a0d7a
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case07.txt
@@ -0,0 +1,8 @@
+# Input for testoplock, case 07
+# Modeled after smbtorture smb2.lease.upgrade
+open 1 3
+req 1 0x803
+show
+open 2 3
+req 2 0x807
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/case08.ref b/usr/src/cmd/smbsrv/testoplock/case08.ref
new file mode 100644
index 0000000000..7f28032990
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case08.ref
@@ -0,0 +1,36 @@
+open 1 1
+ open 1 OK
+req 1 0x805
+ req oplock fid=1 ret oplock=0x805 status=0x0 (SUCCESS)
+show
+ ol_state=0x15 ( EXCLUSIVE WRITE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease=1 OgState=0x805 Brk=0x0 Excl=Y onlist:
+open 2 2
+ open 2 OK
+brk-open 2
+*smb_oplock_ind_break fid=1 NewLevel=0x1, AckReq=1, ComplStatus=0x0 (SUCCESS)
+ brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
+show
+ ol_state=0x10015 ( BREAK_TO_READ_CACHING EXCLUSIVE WRITE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x805 Brk=0x10000 Excl=Y onlist:
+ fid=2 Lease=2 OgState=0x0 Brk=0x0 Excl=N onlist:
+ack 1
+ ack: break fid=1, newstate=0x801, status=0x0 (SUCCESS)
+show
+ ol_state=0x1 ( READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=2 Lease=2 OgState=0x0 Brk=0x0 Excl=N onlist:
+req 2 0x801
+ req oplock fid=2 ret oplock=0x801 status=0x0 (SUCCESS)
+show
+ ol_state=0x1 ( READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=2 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=2 Lease=2 OgState=0x801 Brk=0x0 Excl=N onlist: R
diff --git a/usr/src/cmd/smbsrv/testoplock/case08.txt b/usr/src/cmd/smbsrv/testoplock/case08.txt
new file mode 100644
index 0000000000..ccc8f48711
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case08.txt
@@ -0,0 +1,13 @@
+# Input for testoplock, case 08
+# Modeled after smbtorture smb2.lease.upgrade3
+# sub-case: "R" "RH" "RW" "R"
+open 1 1
+req 1 0x805
+show
+open 2 2
+brk-open 2
+show
+ack 1
+show
+req 2 0x801
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/case09.ref b/usr/src/cmd/smbsrv/testoplock/case09.ref
new file mode 100644
index 0000000000..4003b133db
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case09.ref
@@ -0,0 +1,27 @@
+open 1
+ open 1 OK
+req 1 0x100
+ req oplock fid=1 ret oplock=0x100 status=0x0 (SUCCESS)
+show
+ ol_state=0x100 ( LEVEL_TWO_OPLOCK )
+ Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
+open 2 2
+ open 2 OK
+brk-open 2
+ brk-open 2 ret status=0x0 (SUCCESS)
+show
+ ol_state=0x100 ( LEVEL_TWO_OPLOCK )
+ Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
+ fid=2 Lease=2 OgState=0x0 Brk=0x0 Excl=N onlist:
+req 2 0x803
+ req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED)
+show
+ ol_state=0x100 ( LEVEL_TWO_OPLOCK )
+ Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
+ fid=2 Lease=2 OgState=0x0 Brk=0x0 Excl=N onlist:
diff --git a/usr/src/cmd/smbsrv/testoplock/case09.txt b/usr/src/cmd/smbsrv/testoplock/case09.txt
new file mode 100644
index 0000000000..3683d3d83f
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case09.txt
@@ -0,0 +1,10 @@
+# Input for testoplock, case 09
+# Modeled after smbtorture smb2.lease.oplock
+open 1
+req 1 0x100
+show
+open 2 2
+brk-open 2
+show
+req 2 0x803
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/case10.ref b/usr/src/cmd/smbsrv/testoplock/case10.ref
new file mode 100644
index 0000000000..9e0275d258
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case10.ref
@@ -0,0 +1,31 @@
+open 1
+ open 1 OK
+req 1 0x100
+ req oplock fid=1 ret oplock=0x100 status=0x0 (SUCCESS)
+show
+ ol_state=0x100 ( LEVEL_TWO_OPLOCK )
+ Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
+open 2 2
+ open 2 OK
+brk-open 2
+ brk-open 2 ret status=0x0 (SUCCESS)
+show
+ ol_state=0x100 ( LEVEL_TWO_OPLOCK )
+ Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
+ fid=2 Lease=2 OgState=0x0 Brk=0x0 Excl=N onlist:
+req 2 0x807
+ req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED)
+req 2 0x803
+ req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED)
+req 2 0x801
+ req oplock fid=2 ret oplock=0x801 status=0x0 (SUCCESS)
+show
+ ol_state=0x101 ( LEVEL_TWO_OPLOCK READ_CACHING )
+ Excl=n cnt_II=1 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
+ fid=2 Lease=2 OgState=0x801 Brk=0x0 Excl=N onlist: R
diff --git a/usr/src/cmd/smbsrv/testoplock/case10.txt b/usr/src/cmd/smbsrv/testoplock/case10.txt
new file mode 100644
index 0000000000..10da33e970
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case10.txt
@@ -0,0 +1,12 @@
+# Input for testoplock, case 10
+# Modeled after smbtorture smb2.lease.oplock
+open 1
+req 1 0x100
+show
+open 2 2
+brk-open 2
+show
+req 2 0x807
+req 2 0x803
+req 2 0x801
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/case11.ref b/usr/src/cmd/smbsrv/testoplock/case11.ref
new file mode 100644
index 0000000000..05bae87e04
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case11.ref
@@ -0,0 +1,30 @@
+open 1 1
+ open 1 OK
+req 1 0x807
+ req oplock fid=1 ret oplock=0x807 status=0x0 (SUCCESS)
+show
+ ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease=1 OgState=0x807 Brk=0x0 Excl=Y onlist:
+open 2
+ open 2 OK
+brk-open 2
+*smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS)
+ brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
+ack 1
+ ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=2 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+req 2 0x100
+ req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=2 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
diff --git a/usr/src/cmd/smbsrv/testoplock/case11.txt b/usr/src/cmd/smbsrv/testoplock/case11.txt
new file mode 100644
index 0000000000..7e09c98d73
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case11.txt
@@ -0,0 +1,11 @@
+# Input for testoplock, case 11
+# Modeled after smbtorture smb2.lease.break2
+open 1 1
+req 1 0x807
+show
+open 2
+brk-open 2
+ack 1
+show
+req 2 0x100
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/case12.ref b/usr/src/cmd/smbsrv/testoplock/case12.ref
new file mode 100644
index 0000000000..bc36b7b6de
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case12.ref
@@ -0,0 +1,54 @@
+open 1 1
+ open 1 OK
+req 1 0x807
+ req oplock fid=1 ret oplock=0x807 status=0x0 (SUCCESS)
+show
+ ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease=1 OgState=0x807 Brk=0x0 Excl=Y onlist:
+open 2
+ open 2 OK
+brk-open 2
+*smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS)
+ brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
+waiters 2 1
+ waiters 0 -> 1
+show
+ ol_state=0x30017 ( BREAK_TO_HANDLE_CACHING BREAK_TO_READ_CACHING EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x807 Brk=0x30000 Excl=Y onlist:
+ fid=2 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+open 3
+ open 3 OK
+brk-open 3 4
+ brk-open 3 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
+waiters 3 2
+ waiters 1 -> 2
+show
+ ol_state=0x80017 ( BREAK_TO_NO_CACHING EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=3
+ fid=1 Lease=1 OgState=0x807 Brk=0x30000 Excl=Y onlist:
+ fid=2 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=3 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ack 1 0x803
+*smb_oplock_ind_break fid=1 NewLevel=0x0, AckReq=1, ComplStatus=0x8000002e (CANNOT_GRANT_REQUESTED_OPLOCK)
+ ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS)
+show
+ ol_state=0x80003 ( BREAK_TO_NO_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=1
+ ofile_cnt=3
+ fid=1 Lease=1 OgState=0x803 Brk=0x80000 Excl=N onlist: RHBQ(to none)
+ fid=2 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=3 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ack 1 0x800
+ ack: break fid=1, newstate=0x800, status=0x0 (SUCCESS)
+show
+ ol_state=0x10000000 ( NO_OPLOCK )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=3
+ fid=1 Lease=1 OgState=0x800 Brk=0x0 Excl=N onlist:
+ fid=2 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=3 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
diff --git a/usr/src/cmd/smbsrv/testoplock/case12.txt b/usr/src/cmd/smbsrv/testoplock/case12.txt
new file mode 100644
index 0000000000..23b4f63092
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case12.txt
@@ -0,0 +1,28 @@
+# Input for testoplock, case 12
+# simulate smbtorture smb2.lease.breaking3
+#
+open 1 1
+req 1 0x807
+show
+#
+# a conflicting open (no oplock) is blocked until lease break ack
+open 2
+brk-open 2
+waiters 2 1
+show
+# should see lease break RWH to RH, and brk-open would block.
+# now a conflicting open with disp=overwrite(4), no oplock
+open 3
+brk-open 3 4
+waiters 3 2
+show
+# should see break_to_none pending (but no break ind yet)
+# and brk-open shoud block (break in progress)
+#
+# ack the first lease break above (RWH to RH)
+# should get a new break ind. (RH to none)
+ack 1 0x803
+show
+# got break ind?
+ack 1 0x800
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/case13.ref b/usr/src/cmd/smbsrv/testoplock/case13.ref
new file mode 100644
index 0000000000..a35a5992ce
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case13.ref
@@ -0,0 +1,56 @@
+open 1 1
+ open 1 OK
+req 1 0x801
+ req oplock fid=1 ret oplock=0x801 status=0x0 (SUCCESS)
+show
+ ol_state=0x1 ( READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease=1 OgState=0x801 Brk=0x0 Excl=N onlist: R
+
+open 2 1
+ open 2 OK
+brk-open 2
+ brk-open 2 ret status=0x0 (SUCCESS)
+req 2 0x807
+*smb_oplock_ind_break fid=1 NewLevel=0x7, AckReq=0, ComplStatus=0x215 (OPLOCK_SWITCHED_TO_NEW_HANDLE)
+ req oplock fid=2 ret oplock=0x807 status=0x0 (SUCCESS)
+show
+ ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=2) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=2 Lease=1 OgState=0x807 Brk=0x0 Excl=Y onlist:
+
+move 2 1
+ move 2 1
+close 2
+ close OK
+show
+ ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease=1 OgState=0x807 Brk=0x0 Excl=Y onlist:
+
+open 3 2
+ open 3 OK
+brk-open 3
+*smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS)
+ brk-open 3 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
+ack 1
+ ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=3 Lease=2 OgState=0x0 Brk=0x0 Excl=N onlist:
+
+req 3 0x801
+ req oplock fid=3 ret oplock=0x801 status=0x0 (SUCCESS)
+show
+ ol_state=0x23 ( MIXED_R_AND_RH HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=1 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=3 Lease=2 OgState=0x801 Brk=0x0 Excl=N onlist: R
diff --git a/usr/src/cmd/smbsrv/testoplock/case13.txt b/usr/src/cmd/smbsrv/testoplock/case13.txt
new file mode 100644
index 0000000000..c8d012690d
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case13.txt
@@ -0,0 +1,25 @@
+# Input for testoplock, case 13
+# simulate smbtorture smb2.lease.complex1
+#
+open 1 1
+req 1 0x801
+show
+
+# upgrade lease 1
+open 2 1
+brk-open 2
+req 2 0x807
+show
+
+move 2 1
+close 2
+show
+
+# contend via lease2
+open 3 2
+brk-open 3
+ack 1
+show
+
+req 3 0x801
+show
diff --git a/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_kproto.h b/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_kproto.h
new file mode 100644
index 0000000000..4ccf839f51
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_kproto.h
@@ -0,0 +1,111 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Function prototypes needed by the "testoplock" program
+ * (a small subset of what the SMB server uses)
+ */
+
+#ifndef _SMB_KPROTO_H_
+#define _SMB_KPROTO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/varargs.h>
+#include <sys/cmn_err.h>
+#include <smbsrv/smb.h>
+#include <smbsrv/smb_ktypes.h>
+
+boolean_t smb_ofile_is_open(smb_ofile_t *);
+boolean_t smb_node_is_file(smb_node_t *);
+
+/*
+ * SMB locked list function prototypes
+ */
+void smb_llist_init(void);
+void smb_llist_fini(void);
+void smb_llist_constructor(smb_llist_t *, size_t, size_t);
+void smb_llist_destructor(smb_llist_t *);
+void smb_llist_enter(smb_llist_t *ll, krw_t);
+void smb_llist_exit(smb_llist_t *);
+void smb_llist_post(smb_llist_t *, void *, smb_dtorproc_t);
+void smb_llist_flush(smb_llist_t *);
+void smb_llist_insert_head(smb_llist_t *ll, void *obj);
+void smb_llist_insert_tail(smb_llist_t *ll, void *obj);
+void smb_llist_remove(smb_llist_t *ll, void *obj);
+int smb_llist_upgrade(smb_llist_t *ll);
+uint32_t smb_llist_get_count(smb_llist_t *ll);
+#define smb_llist_head(ll) list_head(&(ll)->ll_list)
+#define smb_llist_next(ll, obj) list_next(&(ll)->ll_list, obj)
+int smb_account_connected(smb_user_t *user);
+
+/*
+ * Common oplock functions
+ */
+uint32_t smb_oplock_request(smb_request_t *, smb_ofile_t *, uint32_t *);
+uint32_t smb_oplock_ack_break(smb_request_t *, smb_ofile_t *, uint32_t *);
+uint32_t smb_oplock_break_PARENT(smb_node_t *, smb_ofile_t *);
+uint32_t smb_oplock_break_OPEN(smb_node_t *, smb_ofile_t *,
+ uint32_t DesiredAccess, uint32_t CreateDisposition);
+uint32_t smb_oplock_break_BATCH(smb_node_t *, smb_ofile_t *,
+ uint32_t DesiredAccess, uint32_t CreateDisposition);
+uint32_t smb_oplock_break_HANDLE(smb_node_t *, smb_ofile_t *);
+void smb_oplock_break_CLOSE(smb_node_t *, smb_ofile_t *);
+uint32_t smb_oplock_break_READ(smb_node_t *, smb_ofile_t *);
+uint32_t smb_oplock_break_WRITE(smb_node_t *, smb_ofile_t *);
+uint32_t smb_oplock_break_SETINFO(smb_node_t *,
+ smb_ofile_t *ofile, uint32_t InfoClass);
+uint32_t smb_oplock_break_DELETE(smb_node_t *, smb_ofile_t *);
+
+void smb_oplock_move(smb_node_t *, smb_ofile_t *, smb_ofile_t *);
+
+/*
+ * Protocol-specific oplock functions
+ * (and "server-level" functions)
+ */
+void smb1_oplock_acquire(smb_request_t *, boolean_t);
+void smb1_oplock_break_notification(smb_request_t *, uint32_t);
+void smb2_oplock_break_notification(smb_request_t *, uint32_t);
+void smb2_lease_break_notification(smb_request_t *, uint32_t, boolean_t);
+void smb_oplock_ind_break(smb_ofile_t *, uint32_t, boolean_t, uint32_t);
+void smb_oplock_ind_break_in_ack(smb_request_t *, smb_ofile_t *,
+ uint32_t, boolean_t);
+void smb_oplock_send_brk(smb_request_t *);
+uint32_t smb_oplock_wait_break(smb_node_t *, int);
+
+int smb_lock_range_access(smb_request_t *, smb_node_t *,
+ uint64_t, uint64_t, boolean_t);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMB_KPROTO_H_ */
diff --git a/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_ktypes.h b/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_ktypes.h
new file mode 100644
index 0000000000..16c770ef1d
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_ktypes.h
@@ -0,0 +1,227 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Structures and type definitions needed by the "testoplock" program
+ * (a small subset of what the SMB server uses)
+ */
+
+#ifndef _SMB_KTYPES_H
+#define _SMB_KTYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/debug.h>
+#include <sys/systm.h>
+#include <sys/cred.h>
+#include <sys/list.h>
+#include <sys/sdt.h>
+
+typedef struct smb_session smb_session_t;
+typedef struct smb_user smb_user_t;
+typedef struct smb_tree smb_tree_t;
+
+
+/*
+ * Destructor object used in the locked-list delete queue.
+ */
+#define SMB_DTOR_MAGIC 0x44544F52 /* DTOR */
+#define SMB_DTOR_VALID(d) \
+ ASSERT(((d) != NULL) && ((d)->dt_magic == SMB_DTOR_MAGIC))
+
+typedef void (*smb_dtorproc_t)(void *);
+
+typedef struct smb_dtor {
+ list_node_t dt_lnd;
+ uint32_t dt_magic;
+ void *dt_object;
+ smb_dtorproc_t dt_proc;
+} smb_dtor_t;
+
+typedef struct smb_llist {
+ krwlock_t ll_lock;
+ list_t ll_list;
+ uint32_t ll_count;
+ uint64_t ll_wrop;
+ kmutex_t ll_mutex;
+ list_t ll_deleteq;
+ uint32_t ll_deleteq_count;
+ boolean_t ll_flushing;
+} smb_llist_t;
+
+/*
+ * Per smb_node oplock state
+ */
+typedef struct smb_oplock {
+ kmutex_t ol_mutex;
+ boolean_t ol_fem; /* fem monitor installed? */
+ struct smb_ofile *excl_open;
+ uint32_t ol_state;
+ int32_t cnt_II;
+ int32_t cnt_R;
+ int32_t cnt_RH;
+ int32_t cnt_RHBQ;
+ int32_t waiters;
+ kcondvar_t WaitingOpenCV;
+} smb_oplock_t;
+
+/*
+ * Per smb_ofile oplock state
+ */
+typedef struct smb_oplock_grant {
+ /* smb protocol-level state */
+ uint32_t og_state; /* latest sent to client */
+ uint32_t og_breaking; /* BREAK_TO... flags */
+ uint16_t og_dialect; /* how to send breaks */
+ /* File-system level state */
+ uint8_t onlist_II;
+ uint8_t onlist_R;
+ uint8_t onlist_RH;
+ uint8_t onlist_RHBQ;
+ uint8_t BreakingToRead;
+} smb_oplock_grant_t;
+
+#define SMB_LEASE_KEY_SZ 16
+
+#define SMB_NODE_MAGIC 0x4E4F4445 /* 'NODE' */
+#define SMB_NODE_VALID(p) ASSERT((p)->n_magic == SMB_NODE_MAGIC)
+
+typedef enum {
+ SMB_NODE_STATE_AVAILABLE = 0,
+ SMB_NODE_STATE_DESTROYING
+} smb_node_state_t;
+
+/*
+ * waiting_event # of clients requesting FCN
+ * n_timestamps cached timestamps
+ * n_allocsz cached file allocation size
+ * n_dnode directory node
+ * n_unode unnamed stream node
+ * delete_on_close_cred credentials for delayed delete
+ */
+typedef struct smb_node {
+ list_node_t n_lnd;
+ uint32_t n_magic;
+ krwlock_t n_lock;
+ kmutex_t n_mutex;
+ smb_node_state_t n_state;
+ uint32_t n_refcnt;
+ uint32_t n_open_count;
+ volatile int flags;
+
+ smb_llist_t n_ofile_list;
+ smb_oplock_t n_oplock;
+} smb_node_t;
+
+#define NODE_FLAGS_WRITE_THROUGH 0x00100000
+#define NODE_FLAGS_DELETE_COMMITTED 0x20000000
+#define NODE_FLAGS_DELETE_ON_CLOSE 0x40000000
+
+/*
+ * Some flags for ofile structure
+ *
+ * SMB_OFLAGS_SET_DELETE_ON_CLOSE
+ * Set this flag when the corresponding open operation whose
+ * DELETE_ON_CLOSE bit of the CreateOptions is set. If any
+ * open file instance has this bit set, the NODE_FLAGS_DELETE_ON_CLOSE
+ * will be set for the file node upon close.
+ */
+
+/* SMB_OFLAGS_READONLY 0x0001 (obsolete) */
+#define SMB_OFLAGS_EXECONLY 0x0002
+#define SMB_OFLAGS_SET_DELETE_ON_CLOSE 0x0004
+#define SMB_OFLAGS_LLF_POS_VALID 0x0008
+
+#define SMB_OFILE_MAGIC 0x4F464C45 /* 'OFLE' */
+#define SMB_OFILE_VALID(p) \
+ ASSERT((p != NULL) && ((p)->f_magic == SMB_OFILE_MAGIC))
+
+/*
+ * This is the size of the per-handle "Lock Sequence" array.
+ * See LockSequenceIndex in [MS-SMB2] 2.2.26, and smb2_lock.c
+ */
+#define SMB_OFILE_LSEQ_MAX 64
+
+/* {arg_open,ofile}->dh_vers values */
+typedef enum {
+ SMB2_NOT_DURABLE = 0,
+ SMB2_DURABLE_V1,
+ SMB2_DURABLE_V2,
+ SMB2_RESILIENT,
+} smb_dh_vers_t;
+
+/*
+ * See the long "Ofile State Machine" comment in smb_ofile.c
+ */
+typedef enum {
+ SMB_OFILE_STATE_ALLOC = 0,
+ SMB_OFILE_STATE_OPEN,
+ SMB_OFILE_STATE_SAVE_DH,
+ SMB_OFILE_STATE_SAVING,
+ SMB_OFILE_STATE_CLOSING,
+ SMB_OFILE_STATE_CLOSED,
+ SMB_OFILE_STATE_ORPHANED,
+ SMB_OFILE_STATE_RECONNECT,
+ SMB_OFILE_STATE_EXPIRED,
+ SMB_OFILE_STATE_SENTINEL
+} smb_ofile_state_t;
+
+typedef struct smb_ofile {
+ list_node_t f_tree_lnd; /* t_ofile_list */
+ list_node_t f_node_lnd; /* n_ofile_list */
+ list_node_t f_dh_lnd; /* sv_persistid_ht */
+ uint32_t f_magic;
+ kmutex_t f_mutex;
+ smb_ofile_state_t f_state;
+
+ uint16_t f_fid;
+ uint16_t f_ftype;
+ uint32_t f_refcnt;
+ uint32_t f_granted_access;
+ uint32_t f_share_access;
+
+ smb_node_t *f_node;
+
+ smb_oplock_grant_t f_oplock;
+ uint8_t TargetOplockKey[SMB_LEASE_KEY_SZ];
+ uint8_t ParentOplockKey[SMB_LEASE_KEY_SZ];
+ struct smb_lease *f_lease;
+
+} smb_ofile_t;
+
+typedef struct smb_request {
+ list_node_t sr_session_lnd;
+ uint32_t sr_magic;
+ kmutex_t sr_mutex;
+} smb_request_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMB_KTYPES_H */
diff --git a/usr/src/cmd/smbsrv/testoplock/tol_all.d b/usr/src/cmd/smbsrv/testoplock/tol_all.d
new file mode 100644
index 0000000000..3a8f2aef84
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/tol_all.d
@@ -0,0 +1,106 @@
+#!/usr/sbin/dtrace -s
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * User-level dtrace for testoplock
+ * Usage: dtrace -s tol.d -c ./testoplock
+ */
+
+#pragma D option flowindent
+
+self int trace;
+self int mask;
+
+/*
+ * Trace almost everything
+ */
+pid$target:testoplock::entry
+{
+ self->trace++;
+}
+
+/*
+ * If traced and not masked, print entry/return
+ */
+pid$target:testoplock::entry
+/self->trace > 0 && self->mask == 0/
+{
+ printf("\t0x%x", arg0);
+ printf("\t0x%x", arg1);
+ printf("\t0x%x", arg2);
+ printf("\t0x%x", arg3);
+ printf("\t0x%x", arg4);
+ printf("\t0x%x", arg5);
+}
+
+/* Skip the bsearch calls. */
+pid$target:testoplock:xlate_nt_status:entry
+{
+ self->mask++;
+}
+
+pid$target:testoplock:xlate_nt_status:return
+{
+ self->mask--;
+}
+
+pid$target:testoplock::return
+/self->trace > 0 && self->mask == 0/
+{
+ printf("\t0x%x", arg1);
+}
+
+pid$target:testoplock::return
+{
+ self->trace--;
+}
+
+/* ---------------------- */
+
+pid$target::smb_oplock_request:entry
+{
+ self->sr = arg0;
+ self->of = arg1;
+ self->statep = arg2;
+ this->state = *(uint32_t *)copyin(self->statep, 4);
+ printf(" entry state=0x%x\n", this->state);
+}
+
+pid$target::smb_oplock_request:return
+{
+ this->sr = (userland pid`smb_request_t *)self->sr;
+ this->state = *(uint32_t *)copyin(self->statep, 4);
+ printf(" return state=0x%x\n", this->state);
+ printf("\nsr->arg.open = ");
+ print(this->sr->arg.open);
+}
+
+pid$target::smb_oplock_break_cmn:entry
+{
+ this->node = (userland pid`smb_node_t *)arg0;
+ this->ofile = (userland pid`smb_ofile_t *)arg1;
+ printf("\nnode->n_oplock = ");
+ print(this->node->n_oplock);
+ printf("\nofile->f_oplock = ");
+ print(this->ofile->f_oplock);
+}
+
+pid$target::smb_oplock_ind_break:entry
+{
+ this->ofile = (userland pid`smb_ofile_t *)arg0;
+ printf("\nofile->f_oplock = ");
+ print(this->ofile->f_oplock);
+}
diff --git a/usr/src/cmd/smbsrv/testoplock/tol_main.c b/usr/src/cmd/smbsrv/testoplock/tol_main.c
new file mode 100644
index 0000000000..575c1f44fd
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/tol_main.c
@@ -0,0 +1,641 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Test & debug program for oplocks
+ *
+ * This implements a simple command reader which accepts
+ * commands to simulate oplock events, and prints the
+ * state changes and actions that would happen after
+ * each event.
+ */
+
+#include <sys/types.h>
+#include <sys/debug.h>
+#include <sys/stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb_oplock.h>
+
+#define OPLOCK_CACHE_RWH (READ_CACHING | HANDLE_CACHING | WRITE_CACHING)
+#define OPLOCK_TYPE (LEVEL_TWO_OPLOCK | LEVEL_ONE_OPLOCK |\
+ BATCH_OPLOCK | OPLOCK_LEVEL_GRANULAR)
+
+#define MAXFID 10
+
+smb_node_t root_node, test_node;
+smb_ofile_t ofile_array[MAXFID];
+smb_request_t test_sr;
+uint32_t last_ind_break_level;
+char cmdbuf[100];
+
+extern const char *xlate_nt_status(uint32_t);
+
+#define BIT_DEF(name) { name, #name }
+
+struct bit_defs {
+ uint32_t mask;
+ const char *name;
+} state_bits[] = {
+ BIT_DEF(NO_OPLOCK),
+ BIT_DEF(BREAK_TO_NO_CACHING),
+ BIT_DEF(BREAK_TO_WRITE_CACHING),
+ BIT_DEF(BREAK_TO_HANDLE_CACHING),
+ BIT_DEF(BREAK_TO_READ_CACHING),
+ BIT_DEF(BREAK_TO_TWO_TO_NONE),
+ BIT_DEF(BREAK_TO_NONE),
+ BIT_DEF(BREAK_TO_TWO),
+ BIT_DEF(BATCH_OPLOCK),
+ BIT_DEF(LEVEL_ONE_OPLOCK),
+ BIT_DEF(LEVEL_TWO_OPLOCK),
+ BIT_DEF(MIXED_R_AND_RH),
+ BIT_DEF(EXCLUSIVE),
+ BIT_DEF(WRITE_CACHING),
+ BIT_DEF(HANDLE_CACHING),
+ BIT_DEF(READ_CACHING),
+ { 0, NULL }
+};
+
+/*
+ * Helper to print flags fields
+ */
+static void
+print_bits32(char *label, struct bit_defs *bit, uint32_t state)
+{
+ printf("%s0x%x (", label, state);
+ while (bit->mask != 0) {
+ if ((state & bit->mask) != 0)
+ printf(" %s", bit->name);
+ bit++;
+ }
+ printf(" )\n");
+}
+
+/*
+ * Command language:
+ *
+ */
+const char helpstr[] = "Commands:\n"
+ "help\t\tList commands\n"
+ "show\t\tShow OpLock state etc.\n"
+ "open FID\n"
+ "close FID\n"
+ "req FID [OplockLevel]\n"
+ "ack FID [OplockLevel]\n"
+ "brk-parent FID\n"
+ "brk-open [OverWrite]\n"
+ "brk-handle FID\n"
+ "brk-read FID\n"
+ "brk-write FID\n"
+ "brk-setinfo FID [InfoClass]\n"
+ "move FID1 FID2\n"
+ "waiters FID [count]\n";
+
+/*
+ * Command handlers
+ */
+
+static void
+do_show(void)
+{
+ smb_node_t *node = &test_node;
+ smb_oplock_t *ol = &node->n_oplock;
+ uint32_t state = ol->ol_state;
+ smb_ofile_t *f;
+
+ print_bits32(" ol_state=", state_bits, state);
+
+ if (ol->excl_open != NULL)
+ printf(" Excl=Y (FID=%d)", ol->excl_open->f_fid);
+ else
+ printf(" Excl=n");
+ printf(" cnt_II=%d cnt_R=%d cnt_RH=%d cnt_RHBQ=%d\n",
+ ol->cnt_II, ol->cnt_R, ol->cnt_RH, ol->cnt_RHBQ);
+
+ printf(" ofile_cnt=%d\n", node->n_ofile_list.ll_count);
+ FOREACH_NODE_OFILE(node, f) {
+ smb_oplock_grant_t *og = &f->f_oplock;
+ printf(" fid=%d Lease=%s OgState=0x%x Brk=0x%x",
+ f->f_fid,
+ f->TargetOplockKey, /* lease */
+ f->f_oplock.og_state,
+ f->f_oplock.og_breaking);
+ printf(" Excl=%s onlist: %s %s %s",
+ (ol->excl_open == f) ? "Y" : "N",
+ og->onlist_II ? "II" : "",
+ og->onlist_R ? "R" : "",
+ og->onlist_RH ? "RH" : "");
+ if (og->onlist_RHBQ) {
+ printf(" RHBQ(to %s)",
+ og->BreakingToRead ?
+ "read" : "none");
+ }
+ printf("\n");
+ }
+}
+
+static void
+do_open(int fid, char *arg2)
+{
+ smb_node_t *node = &test_node;
+ smb_ofile_t *ofile = &ofile_array[fid];
+
+ /*
+ * Simulate an open (minimal init)
+ */
+ if (ofile->f_refcnt) {
+ printf("open fid %d already opened\n");
+ return;
+ }
+
+ if (arg2 != NULL)
+ strlcpy((char *)ofile->TargetOplockKey, arg2,
+ SMB_LEASE_KEY_SZ);
+
+ ofile->f_refcnt++;
+ node->n_open_count++;
+ smb_llist_insert_tail(&node->n_ofile_list, ofile);
+ printf(" open %d OK\n", fid);
+}
+
+static void
+do_close(int fid)
+{
+ smb_node_t *node = &test_node;
+ smb_ofile_t *ofile = &ofile_array[fid];
+
+ /*
+ * Simulate an close
+ */
+ if (ofile->f_refcnt <= 0) {
+ printf(" close fid %d already closed\n");
+ return;
+ }
+ smb_oplock_break_CLOSE(ofile->f_node, ofile);
+
+ smb_llist_remove(&node->n_ofile_list, ofile);
+ node->n_open_count--;
+ ofile->f_refcnt--;
+
+ bzero(ofile->TargetOplockKey, SMB_LEASE_KEY_SZ);
+
+ printf(" close OK\n");
+}
+
+static void
+do_req(int fid, char *arg2)
+{
+ smb_ofile_t *ofile = &ofile_array[fid];
+ uint32_t oplock = BATCH_OPLOCK;
+ uint32_t status;
+
+ if (arg2 != NULL)
+ oplock = strtol(arg2, NULL, 16);
+
+ /*
+ * Request an oplock
+ */
+ status = smb_oplock_request(&test_sr, ofile, &oplock);
+ if (status == 0)
+ ofile->f_oplock.og_state = oplock;
+ printf(" req oplock fid=%d ret oplock=0x%x status=0x%x (%s)\n",
+ fid, oplock, status, xlate_nt_status(status));
+}
+
+
+static void
+do_ack(int fid, char *arg2)
+{
+ smb_ofile_t *ofile = &ofile_array[fid];
+ uint32_t oplock;
+ uint32_t status;
+
+ /* Default to level in last smb_oplock_ind_break() */
+ oplock = last_ind_break_level;
+ if (arg2 != NULL)
+ oplock = strtol(arg2, NULL, 16);
+
+ ofile->f_oplock.og_breaking = 0;
+ status = smb_oplock_ack_break(&test_sr, ofile, &oplock);
+ if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
+ printf(" ack: break fid=%d, break-in-progress\n", fid);
+ ofile->f_oplock.og_state = oplock;
+ }
+ if (status == 0)
+ ofile->f_oplock.og_state = oplock;
+
+ printf(" ack: break fid=%d, newstate=0x%x, status=0x%x (%s)\n",
+ fid, oplock, status, xlate_nt_status(status));
+}
+
+static void
+do_brk_parent(int fid)
+{
+ smb_ofile_t *ofile = &ofile_array[fid];
+ uint32_t status;
+
+ status = smb_oplock_break_PARENT(&test_node, ofile);
+ printf(" brk-parent %d ret status=0x%x (%s)\n",
+ fid, status, xlate_nt_status(status));
+}
+
+static void
+do_brk_open(int fid, char *arg2)
+{
+ smb_ofile_t *ofile = &ofile_array[fid];
+ uint32_t status;
+ int disp = FILE_OPEN;
+
+ if (arg2 != NULL)
+ disp = strtol(arg2, NULL, 16);
+
+ status = smb_oplock_break_OPEN(&test_node, ofile, 7, disp);
+ printf(" brk-open %d ret status=0x%x (%s)\n",
+ fid, status, xlate_nt_status(status));
+}
+
+static void
+do_brk_handle(int fid)
+{
+ smb_ofile_t *ofile = &ofile_array[fid];
+ uint32_t status;
+
+ status = smb_oplock_break_HANDLE(&test_node, ofile);
+ printf(" brk-handle %d ret status=0x%x (%s)\n",
+ fid, status, xlate_nt_status(status));
+
+}
+
+static void
+do_brk_read(int fid)
+{
+ smb_ofile_t *ofile = &ofile_array[fid];
+ uint32_t status;
+
+ status = smb_oplock_break_READ(ofile->f_node, ofile);
+ printf(" brk-read %d ret status=0x%x (%s)\n",
+ fid, status, xlate_nt_status(status));
+}
+
+static void
+do_brk_write(int fid)
+{
+ smb_ofile_t *ofile = &ofile_array[fid];
+ uint32_t status;
+
+ status = smb_oplock_break_WRITE(ofile->f_node, ofile);
+ printf(" brk-write %d ret status=0x%x (%s)\n",
+ fid, status, xlate_nt_status(status));
+}
+
+static void
+do_brk_setinfo(int fid, char *arg2)
+{
+ smb_ofile_t *ofile = &ofile_array[fid];
+ uint32_t status;
+ int infoclass = FileEndOfFileInformation; /* 20 */
+
+ if (arg2 != NULL)
+ infoclass = strtol(arg2, NULL, 16);
+
+ status = smb_oplock_break_SETINFO(
+ &test_node, ofile, infoclass);
+ printf(" brk-setinfo %d ret status=0x%x (%s)\n",
+ fid, status, xlate_nt_status(status));
+
+}
+
+/*
+ * Move oplock to another FD, as specified,
+ * or any other available open
+ */
+static void
+do_move(int fid, char *arg2)
+{
+ smb_ofile_t *ofile = &ofile_array[fid];
+ smb_ofile_t *of2;
+ int fid2;
+
+ if (arg2 == NULL) {
+ fprintf(stderr, "move: FID2 required\n");
+ return;
+ }
+ fid2 = atoi(arg2);
+ if (fid2 <= 0 || fid2 >= MAXFID) {
+ fprintf(stderr, "move: bad FID2 %d\n", fid2);
+ return;
+ }
+ of2 = &ofile_array[fid2];
+
+ smb_oplock_move(&test_node, ofile, of2);
+ printf(" move %d %d\n", fid, fid2);
+}
+
+/*
+ * Set/clear oplock.waiters, which affects ack-break
+ */
+static void
+do_waiters(int fid, char *arg2)
+{
+ smb_node_t *node = &test_node;
+ smb_oplock_t *ol = &node->n_oplock;
+ int old, new = 0;
+
+ if (arg2 != NULL)
+ new = atoi(arg2);
+
+ old = ol->waiters;
+ ol->waiters = new;
+
+ printf(" waiters %d -> %d\n", old, new);
+}
+
+int
+main(int argc, char *argv[])
+{
+ smb_node_t *node = &test_node;
+ char *cmd;
+ char *arg1;
+ char *arg2;
+ char *savep;
+ char *sep = " \t\n";
+ char *prompt = NULL;
+ int fid;
+
+ if (isatty(0))
+ prompt = "> ";
+
+ smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
+ offsetof(smb_ofile_t, f_node_lnd));
+
+ for (fid = 0; fid < MAXFID; fid++) {
+ smb_ofile_t *f = &ofile_array[fid];
+
+ f->f_magic = SMB_OFILE_MAGIC;
+ mutex_init(&f->f_mutex, NULL, MUTEX_DEFAULT, NULL);
+ f->f_fid = fid;
+ f->f_ftype = SMB_FTYPE_DISK;
+ f->f_node = &test_node;
+ }
+
+ for (;;) {
+ if (prompt) {
+ fputs(prompt, stdout);
+ fflush(stdout);
+ }
+
+ cmd = fgets(cmdbuf, sizeof (cmdbuf), stdin);
+ if (cmd == NULL)
+ break;
+ if (cmd[0] == '#')
+ continue;
+
+ if (prompt == NULL) {
+ /* Put commands in the output too. */
+ fputs(cmdbuf, stdout);
+ }
+ cmd = strtok_r(cmd, sep, &savep);
+ if (cmd == NULL)
+ continue;
+
+ /*
+ * Commands with no args
+ */
+ if (0 == strcmp(cmd, "help")) {
+ fputs(helpstr, stdout);
+ continue;
+ }
+
+ if (0 == strcmp(cmd, "show")) {
+ do_show();
+ continue;
+ }
+
+ /*
+ * Commands with one arg (the FID)
+ */
+ arg1 = strtok_r(NULL, sep, &savep);
+ if (arg1 == NULL) {
+ fprintf(stderr, "%s missing arg1\n", cmd);
+ continue;
+ }
+ fid = atoi(arg1);
+ if (fid <= 0 || fid >= MAXFID) {
+ fprintf(stderr, "%s bad FID %d\n", cmd, fid);
+ continue;
+ }
+
+ if (0 == strcmp(cmd, "close")) {
+ do_close(fid);
+ continue;
+ }
+ if (0 == strcmp(cmd, "brk-parent")) {
+ do_brk_parent(fid);
+ continue;
+ }
+ if (0 == strcmp(cmd, "brk-handle")) {
+ do_brk_handle(fid);
+ continue;
+ }
+ if (0 == strcmp(cmd, "brk-read")) {
+ do_brk_read(fid);
+ continue;
+ }
+ if (0 == strcmp(cmd, "brk-write")) {
+ do_brk_write(fid);
+ continue;
+ }
+
+ /*
+ * Commands with an (optional) arg2.
+ */
+ arg2 = strtok_r(NULL, sep, &savep);
+
+ if (0 == strcmp(cmd, "open")) {
+ do_open(fid, arg2);
+ continue;
+ }
+ if (0 == strcmp(cmd, "req")) {
+ do_req(fid, arg2);
+ continue;
+ }
+ if (0 == strcmp(cmd, "ack")) {
+ do_ack(fid, arg2);
+ continue;
+ }
+ if (0 == strcmp(cmd, "brk-open")) {
+ do_brk_open(fid, arg2);
+ continue;
+ }
+ if (0 == strcmp(cmd, "brk-setinfo")) {
+ do_brk_setinfo(fid, arg2);
+ continue;
+ }
+ if (0 == strcmp(cmd, "move")) {
+ do_move(fid, arg2);
+ continue;
+ }
+ if (0 == strcmp(cmd, "waiters")) {
+ do_waiters(fid, arg2);
+ continue;
+ }
+
+ fprintf(stderr, "%s unknown command. Try help\n", cmd);
+ }
+ return (0);
+}
+
+/*
+ * A few functions called by the oplock code
+ * Stubbed out, and/or just print a message.
+ */
+
+boolean_t
+smb_node_is_file(smb_node_t *node)
+{
+ return (B_TRUE);
+}
+
+boolean_t
+smb_ofile_is_open(smb_ofile_t *ofile)
+{
+ return (ofile->f_refcnt != 0);
+}
+
+int
+smb_lock_range_access(
+ smb_request_t *sr,
+ smb_node_t *node,
+ uint64_t start,
+ uint64_t length,
+ boolean_t will_write)
+{
+ return (0);
+}
+
+/*
+ * Test code replacement for: smb_oplock_send_brk()
+ */
+static void
+test_oplock_send_brk(smb_ofile_t *ofile,
+ uint32_t NewLevel, boolean_t AckReq)
+{
+ smb_oplock_grant_t *og = &ofile->f_oplock;
+
+ /* Skip building a message. */
+
+ if ((og->og_state & OPLOCK_LEVEL_GRANULAR) != 0)
+ NewLevel |= OPLOCK_LEVEL_GRANULAR;
+
+ /*
+ * In a real server, we would send a break to the client,
+ * and keep track (at the SMB level) whether this oplock
+ * was obtained via a lease or an old-style oplock.
+ */
+ if (AckReq) {
+ uint32_t BreakTo;
+
+ if ((og->og_state & OPLOCK_LEVEL_GRANULAR) != 0) {
+
+ BreakTo = (NewLevel & CACHE_RWH) << BREAK_SHIFT;
+ if (BreakTo == 0)
+ BreakTo = BREAK_TO_NO_CACHING;
+ } else {
+ if ((NewLevel & LEVEL_TWO_OPLOCK) != 0)
+ BreakTo = BREAK_TO_TWO;
+ else
+ BreakTo = BREAK_TO_NONE;
+ }
+ og->og_breaking = BreakTo;
+ last_ind_break_level = NewLevel;
+ /* Set og_state in do_ack */
+ } else {
+ og->og_state = NewLevel;
+ /* Clear og_breaking in do_ack */
+ }
+}
+
+/*
+ * Simplified version of what's in smb_srv_oplock.c
+ */
+void
+smb_oplock_ind_break(smb_ofile_t *ofile, uint32_t NewLevel,
+ boolean_t AckReq, uint32_t status)
+{
+ smb_oplock_grant_t *og = &ofile->f_oplock;
+
+ printf("*smb_oplock_ind_break fid=%d NewLevel=0x%x,"
+ " AckReq=%d, ComplStatus=0x%x (%s)\n",
+ ofile->f_fid, NewLevel, AckReq,
+ status, xlate_nt_status(status));
+
+ /*
+ * Note that the CompletionStatus from the FS level
+ * (smb_cmn_oplock.c) encodes what kind of action we
+ * need to take at the SMB level.
+ */
+ switch (status) {
+
+ case NT_STATUS_SUCCESS:
+ case NT_STATUS_CANNOT_GRANT_REQUESTED_OPLOCK:
+ test_oplock_send_brk(ofile, NewLevel, AckReq);
+ break;
+
+ case NT_STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE:
+ case NT_STATUS_OPLOCK_HANDLE_CLOSED:
+ og->og_state = OPLOCK_LEVEL_NONE;
+ break;
+
+ default:
+ /* Checked by caller. */
+ ASSERT(0);
+ break;
+ }
+}
+
+void
+smb_oplock_ind_break_in_ack(smb_request_t *sr, smb_ofile_t *ofile,
+ uint32_t NewLevel, boolean_t AckRequired)
+{
+ ASSERT(sr == &test_sr);
+ smb_oplock_ind_break(ofile, NewLevel, AckRequired, STATUS_CANT_GRANT);
+}
+
+uint32_t
+smb_oplock_wait_break(smb_node_t *node, int timeout)
+{
+ printf("*smb_oplock_wait_break (state=0x%x)\n",
+ node->n_oplock.ol_state);
+ return (0);
+}
+
+/*
+ * There are a couple DTRACE_PROBE* in smb_cmn_oplock.c but we're
+ * not linking with the user-level dtrace support, so just
+ * stub these out.
+ */
+void
+__dtrace_fksmb___probe1(char *n, unsigned long a)
+{
+}
+void
+__dtrace_fksmb___probe2(char *n, unsigned long a, unsigned long b)
+{
+}
diff --git a/usr/src/cmd/smbsrv/testoplock/tol_misc.c b/usr/src/cmd/smbsrv/testoplock/tol_misc.c
new file mode 100644
index 0000000000..55a36433ef
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/tol_misc.c
@@ -0,0 +1,179 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * A few excerpts from smb_kutil.c
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/atomic.h>
+#include <sys/debug.h>
+#include <sys/time.h>
+#include <sys/stddef.h>
+#include <smbsrv/smb_kproto.h>
+
+
+/*
+ * smb_llist_constructor
+ *
+ * This function initializes a locked list.
+ */
+void
+smb_llist_constructor(
+ smb_llist_t *ll,
+ size_t size,
+ size_t offset)
+{
+ rw_init(&ll->ll_lock, NULL, RW_DEFAULT, NULL);
+ mutex_init(&ll->ll_mutex, NULL, MUTEX_DEFAULT, NULL);
+ list_create(&ll->ll_list, size, offset);
+ list_create(&ll->ll_deleteq, sizeof (smb_dtor_t),
+ offsetof(smb_dtor_t, dt_lnd));
+ ll->ll_count = 0;
+ ll->ll_wrop = 0;
+ ll->ll_deleteq_count = 0;
+ ll->ll_flushing = B_FALSE;
+}
+
+/*
+ * Flush the delete queue and destroy a locked list.
+ */
+void
+smb_llist_destructor(
+ smb_llist_t *ll)
+{
+ /* smb_llist_flush(ll); */
+
+ ASSERT(ll->ll_count == 0);
+ ASSERT(ll->ll_deleteq_count == 0);
+
+ rw_destroy(&ll->ll_lock);
+ list_destroy(&ll->ll_list);
+ list_destroy(&ll->ll_deleteq);
+ mutex_destroy(&ll->ll_mutex);
+}
+
+void
+smb_llist_enter(smb_llist_t *ll, krw_t mode)
+{
+ rw_enter(&ll->ll_lock, mode);
+}
+
+/*
+ * Exit the list lock and process the delete queue.
+ */
+void
+smb_llist_exit(smb_llist_t *ll)
+{
+ rw_exit(&ll->ll_lock);
+ /* smb_llist_flush(ll); */
+}
+
+/*
+ * smb_llist_upgrade
+ *
+ * This function tries to upgrade the lock of the locked list. It assumes the
+ * locked has already been entered in RW_READER mode. It first tries using the
+ * Solaris function rw_tryupgrade(). If that call fails the lock is released
+ * and reentered in RW_WRITER mode. In that last case a window is opened during
+ * which the contents of the list may have changed. The return code indicates
+ * whether or not the list was modified when the lock was exited.
+ */
+int smb_llist_upgrade(
+ smb_llist_t *ll)
+{
+ uint64_t wrop;
+
+ if (rw_tryupgrade(&ll->ll_lock) != 0) {
+ return (0);
+ }
+ wrop = ll->ll_wrop;
+ rw_exit(&ll->ll_lock);
+ rw_enter(&ll->ll_lock, RW_WRITER);
+ return (wrop != ll->ll_wrop);
+}
+
+/*
+ * smb_llist_insert_head
+ *
+ * This function inserts the object passed a the beginning of the list. This
+ * function assumes the lock of the list has already been entered.
+ */
+void
+smb_llist_insert_head(
+ smb_llist_t *ll,
+ void *obj)
+{
+ list_insert_head(&ll->ll_list, obj);
+ ++ll->ll_wrop;
+ ++ll->ll_count;
+}
+
+/*
+ * smb_llist_insert_tail
+ *
+ * This function appends to the object passed to the list. This function assumes
+ * the lock of the list has already been entered.
+ *
+ */
+void
+smb_llist_insert_tail(
+ smb_llist_t *ll,
+ void *obj)
+{
+ list_insert_tail(&ll->ll_list, obj);
+ ++ll->ll_wrop;
+ ++ll->ll_count;
+}
+
+/*
+ * smb_llist_remove
+ *
+ * This function removes the object passed from the list. This function assumes
+ * the lock of the list has already been entered.
+ */
+void
+smb_llist_remove(
+ smb_llist_t *ll,
+ void *obj)
+{
+ list_remove(&ll->ll_list, obj);
+ ++ll->ll_wrop;
+ --ll->ll_count;
+}
+
+/*
+ * smb_llist_get_count
+ *
+ * This function returns the number of elements in the specified list.
+ */
+uint32_t
+smb_llist_get_count(
+ smb_llist_t *ll)
+{
+ return (ll->ll_count);
+}