summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c295
-rw-r--r--usr/src/cmd/smbsrv/smbd/server.xml2
-rw-r--r--usr/src/common/smbsrv/smb_xdr.c4
-rw-r--r--usr/src/lib/libdtrace/common/smb.d2
-rw-r--r--usr/src/lib/libfakekernel/common/kmisc.c10
-rw-r--r--usr/src/lib/libfakekernel/common/mapfile-vers8
-rw-r--r--usr/src/lib/libfakekernel/common/sys/cred.h4
-rw-r--r--usr/src/lib/smbsrv/libfksmbsrv/Makefile.com1
-rw-r--r--usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_cred.c11
-rw-r--r--usr/src/man/man4/smb.42
-rw-r--r--usr/src/uts/common/Makefile.files1
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_create.c261
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_dispatch.c24
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_durable.c436
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_ioctl.c3
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_logoff.c1
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_oplock.c6
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_session_setup.c17
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_signing.c2
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_authenticate.c34
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_kutil.c199
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_lock.c3
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_node.c12
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_odir.c19
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_ofile.c711
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_opipe.c9
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_oplock.c203
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_server.c337
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_session.c261
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_tree.c115
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_user.c118
-rw-r--r--usr/src/uts/common/smbsrv/smb2.h18
-rw-r--r--usr/src/uts/common/smbsrv/smb2_kproto.h12
-rw-r--r--usr/src/uts/common/smbsrv/smb_kproto.h50
-rw-r--r--usr/src/uts/common/smbsrv/smb_ktypes.h128
-rw-r--r--usr/src/uts/common/smbsrv/smb_xdr.h1
-rw-r--r--usr/src/uts/common/sys/bitmap.h1
37 files changed, 2618 insertions, 703 deletions
diff --git a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c
index d1ec161350..9397a95483 100644
--- a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c
+++ b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c
@@ -571,16 +571,38 @@ smblist_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
typedef struct mdb_smb_server {
smb_server_state_t sv_state;
zoneid_t sv_zid;
+ smb_hash_t *sv_persistid_ht;
} mdb_smb_server_t;
static int
+smb_server_exp_off_sv_list(void)
+{
+ int svl_off, ll_off;
+
+ /* OFFSETOF(smb_server_t, sv_session_list.ll_list); */
+ GET_OFFSET(svl_off, smb_server_t, sv_session_list);
+ GET_OFFSET(ll_off, smb_llist_t, ll_list);
+ return (svl_off + ll_off);
+}
+
+static int
smb_server_exp_off_nbt_list(void)
{
int svd_off, lds_off, ll_off;
/* OFFSETOF(smb_server_t, sv_nbt_daemon.ld_session_list.ll_list); */
GET_OFFSET(svd_off, smb_server_t, sv_nbt_daemon);
- GET_OFFSET(lds_off, smb_listener_daemon_t, ld_session_list);
+ /*
+ * We can't do OFFSETOF() because the member doesn't exist,
+ * but we want backwards compatibility to old cores
+ */
+ lds_off = mdb_ctf_offsetof_by_name("smb_listener_daemon_t",
+ "ld_session_list");
+ if (lds_off < 0) {
+ mdb_warn("cannot lookup: "
+ "smb_listener_daemon_t.ld_session_list");
+ return (-1);
+ }
GET_OFFSET(ll_off, smb_llist_t, ll_list);
return (svd_off + lds_off + ll_off);
}
@@ -592,7 +614,17 @@ smb_server_exp_off_tcp_list(void)
/* OFFSETOF(smb_server_t, sv_tcp_daemon.ld_session_list.ll_list); */
GET_OFFSET(svd_off, smb_server_t, sv_tcp_daemon);
- GET_OFFSET(lds_off, smb_listener_daemon_t, ld_session_list);
+ /*
+ * We can't do OFFSETOF() because the member doesn't exist,
+ * but we want backwards compatibility to old cores
+ */
+ lds_off = mdb_ctf_offsetof_by_name("smb_listener_daemon_t",
+ "ld_session_list");
+ if (lds_off < 0) {
+ mdb_warn("cannot lookup: "
+ "smb_listener_daemon_t.ld_session_list");
+ return (-1);
+ }
GET_OFFSET(ll_off, smb_llist_t, ll_list);
return (svd_off + lds_off + ll_off);
}
@@ -603,6 +635,15 @@ smb_server_exp_off_tcp_list(void)
static const smb_exp_t smb_server_exp[] =
{
{ SMB_OPT_ALL_OBJ,
+ smb_server_exp_off_sv_list,
+ "smbsess", "smb_session"},
+ { 0, 0, NULL, NULL }
+};
+
+/* for backwards compatibility only */
+static const smb_exp_t smb_server_exp_old[] =
+{
+ { SMB_OPT_ALL_OBJ,
smb_server_exp_off_nbt_list,
"smbsess", "smb_session"},
{ SMB_OPT_ALL_OBJ,
@@ -622,6 +663,9 @@ smbsrv_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t opts;
ulong_t indent = 0;
+ const smb_exp_t *sv_exp;
+ mdb_ctf_id_t id;
+ ulong_t off;
if (smb_dcmd_getopt(&opts, argc, argv))
return (DCMD_USAGE);
@@ -668,7 +712,18 @@ smbsrv_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
addr, sv->sv_zid, state);
}
}
- if (smb_obj_expand(addr, opts, smb_server_exp, indent))
+
+ /* if we can't look up the type name, just error out */
+ if (mdb_ctf_lookup_by_name("smb_server_t", &id) == -1)
+ return (DCMD_ERR);
+
+ if (mdb_ctf_offsetof(id, "sv_session_list", &off) == -1)
+ /* sv_session_list doesn't exist; old core */
+ sv_exp = smb_server_exp_old;
+ else
+ sv_exp = smb_server_exp;
+
+ if (smb_obj_expand(addr, opts, sv_exp, indent))
return (DCMD_ERR);
return (DCMD_OK);
}
@@ -892,8 +947,6 @@ typedef struct mdb_smb_request {
unsigned char first_smb_com;
unsigned char smb_com;
- uint16_t smb2_cmd_code;
- uint64_t smb2_messageid;
uint16_t smb_tid;
uint32_t smb_pid;
@@ -901,6 +954,10 @@ typedef struct mdb_smb_request {
uint16_t smb_mid;
uint16_t smb_fid;
+ uint16_t smb2_cmd_code;
+ uint64_t smb2_messageid;
+ uint64_t smb2_ssnid;
+
struct smb_tree *tid_tree;
struct smb_ofile *fid_ofile;
smb_user_t *uid_user;
@@ -990,15 +1047,21 @@ smbreq_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
"state: %u (%s)\n",
sr->sr_state, state);
+ if (sr->smb2_ssnid != 0) {
+ mdb_printf(
+ "SSNID(user): 0x%llx (%p)\n",
+ sr->smb2_ssnid, sr->uid_user);
+ } else {
+ mdb_printf(
+ "UID(user): %u (%p)\n",
+ sr->smb_uid, sr->uid_user);
+ }
+
mdb_printf(
"TID(tree): %u (%p)\n",
sr->smb_tid, sr->tid_tree);
mdb_printf(
- "UID(user): %u (%p)\n",
- sr->smb_uid, sr->uid_user);
-
- mdb_printf(
"FID(file): %u (%p)\n",
sr->smb_fid, sr->fid_ofile);
@@ -1284,6 +1347,7 @@ typedef struct mdb_smb_user {
cred_t *u_cred;
cred_t *u_privcred;
+ uint64_t u_ssnid;
uint32_t u_refcnt;
uint32_t u_flags;
uint32_t u_privileges;
@@ -1387,6 +1451,7 @@ smbuser_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
mdb_printf("%<b>%<u>SMB user information (%p):"
"%</u>%</b>\n", addr);
mdb_printf("UID: %u\n", user->u_uid);
+ mdb_printf("SSNID: %llx\n", user->u_ssnid);
mdb_printf("State: %d (%s)\n", user->u_state, state);
mdb_printf("Flags: 0x%08x <%b>\n", user->u_flags,
user->u_flags, user_flag_bits);
@@ -1401,11 +1466,12 @@ smbuser_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
mdb_printf(
"%<b>%<u>%?-s "
"%-5s "
+ "%-16s "
"%-32s%</u>%</b>\n",
- "USER", "UID", "ACCOUNT");
+ "USER", "UID", "SSNID", "ACCOUNT");
- mdb_printf("%-?p %-5u %-32s\n", addr, user->u_uid,
- account);
+ mdb_printf("%-?p %-5u %-16llx %-32s\n",
+ addr, user->u_uid, user->u_ssnid, account);
}
}
return (DCMD_OK);
@@ -1717,6 +1783,7 @@ typedef struct mdb_smb_ofile {
int f_mode;
cred_t *f_cr;
pid_t f_pid;
+ smb_dh_vers_t dh_vers;
} mdb_smb_ofile_t;
static const mdb_bitmask_t
@@ -1775,16 +1842,23 @@ smbofile_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
}
if (opts & SMB_OPT_VERBOSE) {
char state[40];
+ char durable[40];
get_enum(state, sizeof (state),
"smb_ofile_state_t", of->f_state,
"SMB_OFILE_STATE_");
+ get_enum(durable, sizeof (durable),
+ "smb_dh_vers_t", of->dh_vers,
+ "SMB2_");
+
mdb_printf(
"%<b>%<u>SMB ofile information (%p):%</u>%</b>\n\n",
addr);
mdb_printf("FID: %u\n", of->f_fid);
mdb_printf("State: %d (%s)\n", of->f_state, state);
+ mdb_printf("DH Type: %d (%s)\n", of->dh_vers,
+ durable);
mdb_printf("SMB Node: %p\n", of->f_node);
mdb_printf("LLF Offset: 0x%llx (%s)\n",
of->f_llf_pos,
@@ -1817,6 +1891,174 @@ smbofile_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_OK);
}
+static int
+smbdurable_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ mdb_smb_server_t *sv;
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ mdb_printf("require address of an smb_server_t\n");
+ return (WALK_ERR);
+ }
+
+ sv = mdb_zalloc(sizeof (*sv), UM_SLEEP | UM_GC);
+ if (mdb_ctf_vread(sv, SMBSRV_SCOPE "smb_server_t",
+ "mdb_smb_server_t", addr, 0) < 0) {
+ mdb_warn("failed to read smb_server at %p", addr);
+ return (DCMD_ERR);
+ }
+
+ if (mdb_pwalk_dcmd("smb_hash_walker", "smbofile",
+ argc, argv, (uintptr_t)sv->sv_persistid_ht) == -1) {
+ mdb_warn("failed to walk 'smb_ofile'");
+ return (DCMD_ERR);
+ }
+ return (DCMD_OK);
+}
+
+static int
+smb_hash_walk_init(mdb_walk_state_t *wsp)
+{
+ smb_hash_t hash;
+ int ll_off, sll_off, i;
+ uintptr_t addr = wsp->walk_addr;
+
+ if (addr == NULL) {
+ mdb_printf("require address of an smb_hash_t\n");
+ return (WALK_ERR);
+ }
+
+ GET_OFFSET(sll_off, smb_bucket_t, b_list);
+ GET_OFFSET(ll_off, smb_llist_t, ll_list);
+
+ if (mdb_vread(&hash, sizeof (hash), addr) == -1) {
+ mdb_warn("failed to read smb_hash_t at %p", addr);
+ return (WALK_ERR);
+ }
+
+ for (i = 0; i < hash.num_buckets; i++) {
+ wsp->walk_addr = (uintptr_t)hash.buckets +
+ (i * sizeof (smb_bucket_t)) + sll_off + ll_off;
+ if (mdb_layered_walk("list", wsp) == -1) {
+ mdb_warn("failed to walk 'list'");
+ return (WALK_ERR);
+ }
+ }
+
+ return (WALK_NEXT);
+}
+
+static int
+smb_hash_walk_step(mdb_walk_state_t *wsp)
+{
+ return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
+ wsp->walk_cbdata));
+}
+
+static int
+smbhashstat_cb(uintptr_t addr, const void *data, void *varg)
+{
+ _NOTE(ARGUNUSED(varg))
+ const smb_bucket_t *bucket = data;
+
+ mdb_printf("%-?p ", addr); /* smb_bucket_t */
+ mdb_printf("%-6u ", bucket->b_list.ll_count);
+ mdb_printf("%-16u", bucket->b_max_seen);
+ mdb_printf("%-u\n", (bucket->b_list.ll_wrop +
+ bucket->b_list.ll_count) / 2);
+ return (WALK_NEXT);
+}
+
+static int
+smbhashstat_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ _NOTE(ARGUNUSED(argc, argv))
+ if (!(flags & DCMD_ADDRSPEC)) {
+ mdb_printf("require address of an smb_hash_t\n");
+ return (DCMD_USAGE);
+ }
+
+ if (DCMD_HDRSPEC(flags)) {
+ mdb_printf(
+ "%<b>%<u>"
+ "%-?s "
+ "%-6s "
+ "%-16s"
+ "%-s"
+ "%</u>%</b>\n",
+ "smb_bucket_t", "count", "largest seen", "inserts");
+ }
+
+ if (mdb_pwalk("smb_hashstat_walker", smbhashstat_cb,
+ NULL, addr) == -1) {
+ mdb_warn("failed to walk 'smb_ofile'");
+ return (DCMD_ERR);
+ }
+ return (DCMD_OK);
+}
+
+typedef struct smb_hash_wd {
+ smb_bucket_t *bucket;
+ smb_bucket_t *end;
+} smb_hash_wd_t;
+
+static int
+smb_hashstat_walk_init(mdb_walk_state_t *wsp)
+{
+ int sll_off, ll_off;
+ smb_hash_t hash;
+ smb_bucket_t *buckets;
+ uintptr_t addr = wsp->walk_addr;
+ uint32_t arr_sz;
+ smb_hash_wd_t *wd;
+
+ if (addr == NULL) {
+ mdb_printf("require address of an smb_hash_t\n");
+ return (WALK_ERR);
+ }
+
+ GET_OFFSET(sll_off, smb_bucket_t, b_list);
+ GET_OFFSET(ll_off, smb_llist_t, ll_list);
+
+ if (mdb_vread(&hash, sizeof (hash), addr) == -1) {
+ mdb_warn("failed to read smb_hash_t at %p", addr);
+ return (WALK_ERR);
+ }
+
+ arr_sz = hash.num_buckets * sizeof (smb_bucket_t);
+ buckets = mdb_alloc(arr_sz, UM_SLEEP | UM_GC);
+ if (mdb_vread(buckets, arr_sz, (uintptr_t)hash.buckets) == -1) {
+ mdb_warn("failed to read smb_bucket_t array at %p",
+ hash.buckets);
+ return (WALK_ERR);
+ }
+
+ wd = mdb_alloc(sizeof (*wd), UM_SLEEP | UM_GC);
+ wd->bucket = buckets;
+ wd->end = buckets + hash.num_buckets;
+
+ wsp->walk_addr = (uintptr_t)hash.buckets;
+ wsp->walk_data = wd;
+
+ return (WALK_NEXT);
+}
+
+static int
+smb_hashstat_walk_step(mdb_walk_state_t *wsp)
+{
+ int rc;
+ smb_hash_wd_t *wd = wsp->walk_data;
+
+ if (wd->bucket >= wd->end)
+ return (WALK_DONE);
+
+ rc = wsp->walk_callback(wsp->walk_addr, wd->bucket++,
+ wsp->walk_cbdata);
+
+ wsp->walk_addr += sizeof (smb_bucket_t);
+ return (rc);
+}
+
/*
* *****************************************************************************
* ******************************** smb_kshare_t *******************************
@@ -3168,6 +3410,13 @@ smb_obj_expand(uintptr_t addr, uint_t opts, const smb_exp_t *x, ulong_t indent)
while (x->ex_dcmd) {
if (x->ex_mask & opts) {
ex_off = (x->ex_offset)();
+ if (ex_off < 0) {
+ mdb_warn("failed to get the list offset for %s",
+ x->ex_name);
+ rc = ex_off;
+ break;
+ }
+
rc = mdb_pwalk_dcmd("list", x->ex_dcmd, argc, argv,
addr + ex_off);
@@ -3361,6 +3610,15 @@ static const mdb_dcmd_t dcmds[] = {
{ "smb_mbuf_dump", ":[max_len]",
"print mbuf_t data",
smb_mbuf_dump_dcmd },
+ { "smbdurable",
+ "[-v]",
+ "list ofiles on sv->sv_persistid_ht",
+ smbdurable_dcmd },
+ { "smbhashstat",
+ "[-v]",
+ "list stats from an smb_hash_t structure",
+ smbhashstat_dcmd },
+
{ NULL }
};
@@ -3395,6 +3653,19 @@ static const mdb_walker_t walkers[] = {
smb_mbuf_walk_step,
NULL,
NULL },
+ { "smb_hash_walker",
+ "walk an smb_hash_t structure",
+ smb_hash_walk_init,
+ smb_hash_walk_step,
+ NULL,
+ NULL },
+ { "smb_hashstat_walker",
+ "walk the buckets from an smb_hash_t structure",
+ smb_hashstat_walk_init,
+ smb_hashstat_walk_step,
+ NULL,
+ NULL },
+
{ NULL }
};
diff --git a/usr/src/cmd/smbsrv/smbd/server.xml b/usr/src/cmd/smbsrv/smbd/server.xml
index 875d6d3bc0..7eea0b877b 100644
--- a/usr/src/cmd/smbsrv/smbd/server.xml
+++ b/usr/src/cmd/smbsrv/smbd/server.xml
@@ -184,7 +184,7 @@ file.
<propval name='max_connections' type='integer'
value='100000' override='true'/>
<propval name='keep_alive' type='integer'
- value='5400' override='true'/>
+ value='0' override='true'/>
<propval name='restrict_anonymous' type='boolean'
value='false' override='true'/>
<propval name='signing_enabled' type='boolean'
diff --git a/usr/src/common/smbsrv/smb_xdr.c b/usr/src/common/smbsrv/smb_xdr.c
index 0905e48528..d2653a728b 100644
--- a/usr/src/common/smbsrv/smb_xdr.c
+++ b/usr/src/common/smbsrv/smb_xdr.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/sunddi.h>
@@ -238,8 +238,6 @@ smb_netuserinfo_xdr(XDR *xdrs, smb_netuserinfo_t *objp)
{
if (!xdr_uint64_t(xdrs, &objp->ui_session_id))
return (FALSE);
- if (!xdr_uint16_t(xdrs, &objp->ui_smb_uid))
- return (FALSE);
if (!xdr_uint16_t(xdrs, &objp->ui_domain_len))
return (FALSE);
if (!xdr_string(xdrs, &objp->ui_domain, ~0))
diff --git a/usr/src/lib/libdtrace/common/smb.d b/usr/src/lib/libdtrace/common/smb.d
index 718f7dfa01..c58cb4bf1c 100644
--- a/usr/src/lib/libdtrace/common/smb.d
+++ b/usr/src/lib/libdtrace/common/smb.d
@@ -145,7 +145,7 @@ translator smb2opinfo_t < struct smb_request *P > {
soi_sid = P->session->s_kid;
soi_mid = P->smb2_messageid;
soi_asyncid = P->smb2_async_id;
- soi_uid = P->smb_uid;
+ soi_uid = P->smb2_ssnid;
soi_tid = P->smb_tid;
soi_status = P->smb2_status;
soi_flags = P->smb2_hdr_flags;
diff --git a/usr/src/lib/libfakekernel/common/kmisc.c b/usr/src/lib/libfakekernel/common/kmisc.c
index 5feaa66a28..15730d6539 100644
--- a/usr/src/lib/libfakekernel/common/kmisc.c
+++ b/usr/src/lib/libfakekernel/common/kmisc.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright 2017 RackTop Systems.
*/
@@ -19,13 +19,13 @@
#include <sys/thread.h>
#include <sys/proc.h>
#include <sys/zone.h>
-
#include <sys/poll.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
+#include <string.h>
#include <fakekernel.h>
@@ -118,6 +118,12 @@ delay(clock_t ticks)
(void) poll(0, 0, msec);
}
+int
+highbit(ulong_t i)
+{
+ return (fls(i));
+}
+
/* ARGSUSED */
int
issig(int why)
diff --git a/usr/src/lib/libfakekernel/common/mapfile-vers b/usr/src/lib/libfakekernel/common/mapfile-vers
index 85f9287e06..e8d231159c 100644
--- a/usr/src/lib/libfakekernel/common/mapfile-vers
+++ b/usr/src/lib/libfakekernel/common/mapfile-vers
@@ -10,7 +10,7 @@
#
#
-# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2016 Nexenta Systems, Inc. All rights reserved.
# Copyright 2017 RackTop Systems.
# Copyright 2019 Joyent, Inc.
#
@@ -52,6 +52,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
cyclic_reprogram;
crfree;
+ crgetsid;
crgetuid;
crgetruid;
crgetgid;
@@ -96,10 +97,11 @@ SYMBOL_VERSION SUNWprivate_1.1 {
gethrestime_sec;
gethrtime_unscaled;
- hz;
-
+ highbit;
highbit64;
+ hz;
+
issig;
kcred;
diff --git a/usr/src/lib/libfakekernel/common/sys/cred.h b/usr/src/lib/libfakekernel/common/sys/cred.h
index a338214843..7374dd3df2 100644
--- a/usr/src/lib/libfakekernel/common/sys/cred.h
+++ b/usr/src/lib/libfakekernel/common/sys/cred.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
* Copyright 2017 RackTop Systems.
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
@@ -54,6 +54,7 @@ typedef struct cred cred_t;
cred_t *_curcred(void);
#define CRED() (_curcred()) /* current cred_t pointer */
+struct ksid;
extern int ngroups_max;
@@ -79,6 +80,7 @@ extern gid_t crgetsgid(const cred_t *);
extern zoneid_t crgetzoneid(const cred_t *);
extern struct zone *crgetzone(const cred_t *);
extern projid_t crgetprojid(const cred_t *);
+extern struct ksid *crgetsid(const cred_t *, int);
extern const gid_t *crgetgroups(const cred_t *);
extern int crgetngroups(const cred_t *);
diff --git a/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com b/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com
index 6809bde3c3..db76d55f78 100644
--- a/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com
+++ b/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com
@@ -123,6 +123,7 @@ OBJS_FS_SMBSRV = \
\
smb2_aapl.o \
smb2_dispatch.o \
+ smb2_durable.o \
smb2_cancel.o \
smb2_change_notify.o \
smb2_close.o \
diff --git a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_cred.c b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_cred.c
index 0ddd6f51bc..7b2bb93581 100644
--- a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_cred.c
+++ b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_cred.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -38,11 +38,18 @@
* we don't bother with real credential. Everything here uses
* the ordinary credentials of the process running this.
*/
+
+/*
+ * This library does not implement real credentials. All contexts
+ * use an opaque cred_t object, and all activity happens in the
+ * context of the user who runs the program.
+ */
cred_t *
smb_cred_create(smb_token_t *token)
{
+ _NOTE(ARGUNUSED(token))
cred_t *cr;
- cr = (cred_t *)token; /* hack */
+ cr = CRED();
return (cr);
}
diff --git a/usr/src/man/man4/smb.4 b/usr/src/man/man4/smb.4
index b7073a0f65..27043028b2 100644
--- a/usr/src/man/man4/smb.4
+++ b/usr/src/man/man4/smb.4
@@ -117,7 +117,7 @@ are \fBtrue\fR and \fBfalse\fR. The default value is \fBfalse\fR.
.RS 4n
Specifies the number of seconds before an idle SMB connection is dropped by the
Solaris CIFS server. If set to 0, idle connections are not dropped. Valid
-values are 0 and from 20 seconds and above. The default value is 5400 seconds.
+values are 0 and from 20 seconds and above. The default value is 0.
.RE
.sp
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index c523bf0f77..aa8669de46 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -1198,6 +1198,7 @@ SMBSRV_OBJS += $(SMBSRV_SHARED_OBJS) \
\
smb2_aapl.o \
smb2_dispatch.o \
+ smb2_durable.o \
smb2_cancel.o \
smb2_change_notify.o \
smb2_close.o \
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_create.c b/usr/src/uts/common/fs/smbsrv/smb2_create.c
index fb8a7f8823..f2ad134f84 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_create.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_create.c
@@ -21,6 +21,8 @@
#include <smbsrv/smb2_kproto.h>
#include <smbsrv/smb_fsops.h>
+#define DH_PERSISTENT SMB2_DHANDLE_FLAG_PERSISTENT
+
/*
* Some flags used locally to keep track of which Create Context
* names have been provided and/or requested.
@@ -35,6 +37,8 @@
#define CCTX_QUERY_ON_DISK_ID 0x80
#define CCTX_REQUEST_LEASE 0x100
#define CCTX_AAPL_EXT 0x200
+#define CCTX_DH_REQUEST_V2 0x400
+#define CCTX_DH_RECONNECT_V2 0x800
typedef struct smb2_create_ctx_elem {
uint32_t cce_len;
@@ -54,10 +58,14 @@ typedef struct smb2_create_ctx {
smb2_create_ctx_elem_t cc_in_time_warp;
smb2_create_ctx_elem_t cc_in_req_lease;
smb2_create_ctx_elem_t cc_in_aapl;
+ smb2_create_ctx_elem_t cc_in_dh_request_v2;
+ smb2_create_ctx_elem_t cc_in_dh_reconnect_v2;
/* Elements we my place in the response */
smb2_create_ctx_elem_t cc_out_max_access;
smb2_create_ctx_elem_t cc_out_file_id;
smb2_create_ctx_elem_t cc_out_aapl;
+ smb2_create_ctx_elem_t cc_out_dh_request;
+ smb2_create_ctx_elem_t cc_out_dh_request_v2;
} smb2_create_ctx_t;
static uint32_t smb2_decode_create_ctx(
@@ -68,6 +76,8 @@ static int smb2_encode_create_ctx_elem(
mbuf_chain_t *, smb2_create_ctx_elem_t *, uint32_t);
static void smb2_free_create_ctx(smb2_create_ctx_t *);
+int smb2_enable_dh = 1;
+
smb_sdrc_t
smb2_create(smb_request_t *sr)
{
@@ -86,8 +96,9 @@ smb2_create(smb_request_t *sr)
uint16_t NameLength;
uint32_t CreateCtxOffset;
uint32_t CreateCtxLength;
- smb2fid_t smb2fid;
+ smb2fid_t smb2fid = { 0, 0 };
uint32_t status;
+ int dh_flags;
int skip;
int rc = 0;
@@ -213,6 +224,82 @@ smb2_create(smb_request_t *sr)
*/
/*
+ * Only disk trees get durable handles.
+ */
+ if (smb2_enable_dh == 0 ||
+ (sr->tid_tree->t_res_type & STYPE_MASK) != STYPE_DISKTREE) {
+ cctx.cc_in_flags &=
+ ~(CCTX_DH_REQUEST | CCTX_DH_REQUEST_V2 |
+ CCTX_DH_RECONNECT | CCTX_DH_RECONNECT_V2);
+ }
+
+ /*
+ * DH v2 is only valid in SMB3.0 and later.
+ * If seen in earlier dialects, ignore.
+ */
+ if (sr->session->dialect < SMB_VERS_3_0) {
+ cctx.cc_in_flags &=
+ ~(CCTX_DH_REQUEST_V2|CCTX_DH_RECONNECT_V2);
+ }
+
+ /*
+ * It is an error to specify more than one Durable Handle
+ * operation in a single create, except when only the v1
+ * REQUEST and RECONNECT operations are specified. In that
+ * case, the v1 REQUEST is ignored.
+ */
+ dh_flags = cctx.cc_in_flags &
+ (CCTX_DH_REQUEST | CCTX_DH_REQUEST_V2 |
+ CCTX_DH_RECONNECT | CCTX_DH_RECONNECT_V2);
+ if ((dh_flags & (dh_flags - 1)) != 0 &&
+ dh_flags != (CCTX_DH_REQUEST|CCTX_DH_RECONNECT)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cmd_done;
+ }
+
+ /*
+ * Reconnect is special in MANY ways, including the
+ * somewhat surprising (specified) behavior that
+ * most other creat parameters are ignored, and
+ * many create context types are ignored too.
+ */
+ op->dh_vers = SMB2_NOT_DURABLE;
+ op->dh_v2_flags = 0;
+ if ((cctx.cc_in_flags &
+ (CCTX_DH_RECONNECT|CCTX_DH_RECONNECT_V2)) != 0) {
+
+ if ((cctx.cc_in_flags & CCTX_DH_RECONNECT_V2) != 0)
+ op->dh_vers = SMB2_DURABLE_V2;
+ else
+ op->dh_vers = SMB2_DURABLE_V1;
+
+ /* Ignore these create contexts. */
+ cctx.cc_in_flags &=
+ ~(CCTX_DH_REQUEST |
+ CCTX_DH_REQUEST_V2 |
+ CCTX_EA_BUFFER |
+ CCTX_SD_BUFFER |
+ CCTX_ALLOCATION_SIZE |
+ CCTX_TIMEWARP_TOKEN |
+ CCTX_QUERY_ON_DISK_ID);
+
+ status = smb2_dh_reconnect(sr);
+ if (status != NT_STATUS_SUCCESS)
+ goto cmd_done;
+
+ /*
+ * Skip most open execution during reconnect.
+ */
+ of = sr->fid_ofile;
+
+ goto reconnect_done;
+ }
+
+ /*
+ * Real create (of a new handle, not reconnect)
+ */
+
+ /*
* Validate the requested oplock level.
* Convert the SMB2 oplock level into SMB1 form.
*/
@@ -245,6 +332,14 @@ smb2_create(smb_request_t *sr)
cctx.cc_in_flags &= ~CCTX_REQUEST_LEASE;
}
+ if ((cctx.cc_in_flags &
+ (CCTX_DH_REQUEST|CCTX_DH_REQUEST_V2)) != 0) {
+ if ((cctx.cc_in_flags & CCTX_DH_REQUEST_V2) != 0)
+ op->dh_vers = SMB2_DURABLE_V2;
+ else
+ op->dh_vers = SMB2_DURABLE_V1;
+ }
+
if (cctx.cc_in_flags & CCTX_EA_BUFFER) {
status = NT_STATUS_EAS_NOT_SUPPORTED;
goto cmd_done;
@@ -285,6 +380,66 @@ smb2_create(smb_request_t *sr)
of = sr->fid_ofile;
/*
+ * Set the "persistent" part of the file ID
+ * (only for DISK shares). Need this even for
+ * non-durable handles in case we get the ioctl
+ * to set "resiliency" on this handle.
+ */
+ if (of->f_ftype == SMB_FTYPE_DISK) {
+ smb_ofile_set_persistid(of);
+ }
+
+ /*
+ * We're supposed to process Durable Handle requests
+ * if any one of the following conditions is true:
+ *
+ * 1. op_oplock_level == SMB_OPLOCK_BATCH
+ * 2. A lease is requested with handle caching
+ * - for v1, the lease must not be on a directory
+ * 3. For v2, flags has "persistent" (tree is CA)
+ * (when tree not CA, turned off persist above)
+ *
+ * Otherwise, the requests are ignored.
+ * However, because we don't support leases or CA,
+ * cases 2 and 3 are not of concern to us yet.
+ */
+ if ((cctx.cc_in_flags &
+ (CCTX_DH_REQUEST|CCTX_DH_REQUEST_V2)) != 0 &&
+ smb_node_is_file(of->f_node) &&
+ (op->op_oplock_level == SMB_OPLOCK_BATCH)) {
+ /*
+ * OK, make this handle "durable"
+ */
+ if (op->dh_vers == SMB2_DURABLE_V2) {
+ (void) memcpy(of->dh_create_guid,
+ op->create_guid, UUID_LEN);
+
+ /* no persistent handles yet */
+ of->dh_persist = B_FALSE;
+ }
+ if (op->dh_vers != SMB2_NOT_DURABLE) {
+ uint32_t msto;
+
+ of->dh_vers = op->dh_vers;
+ of->dh_expire_time = 0;
+
+ /*
+ * Client may provide timeout=0 to request
+ * the default timeout (in mSec.)
+ */
+ msto = op->dh_timeout;
+ if (msto == 0)
+ msto = smb2_dh_def_timeout;
+ if (msto > smb2_dh_max_timeout)
+ msto = smb2_dh_max_timeout;
+ op->dh_timeout = msto;
+ of->dh_timeout_offset = MSEC2NSEC(msto);
+ }
+ } else {
+ op->dh_vers = SMB2_NOT_DURABLE;
+ }
+
+ /*
* NB: after the above smb_common_open() success,
* we have a handle allocated (sr->fid_ofile).
* If we don't return success, we must close it.
@@ -293,7 +448,8 @@ smb2_create(smb_request_t *sr)
* though it could later be something larger,
* (16 bytes) similar to an NFSv4 open handle.
*/
- smb2fid.persistent = 0;
+reconnect_done:
+ smb2fid.persistent = of->f_persistid;
smb2fid.temporal = sr->smb_fid;
switch (sr->tid_tree->t_res_type & STYPE_MASK) {
@@ -343,6 +499,15 @@ smb2_create(smb_request_t *sr)
status = 0;
}
+ if ((cctx.cc_in_flags & CCTX_DH_REQUEST) != 0 &&
+ of->dh_vers == SMB2_DURABLE_V1) {
+ cctx.cc_out_flags |= CCTX_DH_REQUEST;
+ }
+ if ((cctx.cc_in_flags & CCTX_DH_REQUEST_V2) != 0 &&
+ of->dh_vers == SMB2_DURABLE_V2) {
+ cctx.cc_out_flags |= CCTX_DH_REQUEST_V2;
+ }
+
/*
* This marks the end of the "body" section and the
* beginning of the "encode" section. Any errors
@@ -554,6 +719,18 @@ smb2_decode_create_ctx(smb_request_t *sr, smb2_create_ctx_t *cc)
cc->cc_in_flags |= CCTX_AAPL_EXT;
cce = &cc->cc_in_aapl;
break;
+ case SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2: /* ("DH2Q") */
+ cc->cc_in_flags |= CCTX_DH_REQUEST_V2;
+ cce = &cc->cc_in_dh_request_v2;
+ break;
+ case SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2: /* ("DH2C") */
+ cc->cc_in_flags |= CCTX_DH_RECONNECT_V2;
+ cce = &cc->cc_in_dh_reconnect_v2;
+ break;
+ case 0x9ccbcf9e: /* SVHDX_OPEN_DEVICE_CONTEXT */
+ /* 9ccbcf9e 04c1e643 980e158d a1f6ec83 */
+ /* silently ignore */
+ break;
default:
/*
* Unknown create context values are normal, and
@@ -613,6 +790,47 @@ smb2_decode_create_ctx(smb_request_t *sr, smb2_create_ctx_t *cc)
op->create_timewarp = B_TRUE;
break;
+ case SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2: /* ("DH2C") */
+ rc = smb_mbc_decodef(&cce->cce_mbc, "qq#cl",
+ &op->dh_fileid.persistent, /* q */
+ &op->dh_fileid.temporal, /* q */
+ UUID_LEN, /* # */
+ op->create_guid, /* c */
+ &op->dh_v2_flags); /* l */
+ if (rc != 0)
+ goto errout;
+ break;
+
+ case SMB2_CREATE_DURABLE_HANDLE_RECONNECT: /* ("DHnC") */
+ rc = smb_mbc_decodef(&cce->cce_mbc, "qq",
+ &op->dh_fileid.persistent, /* q */
+ &op->dh_fileid.temporal); /* q */
+ if (rc != 0)
+ goto errout;
+ bzero(op->create_guid, UUID_LEN);
+ op->dh_v2_flags = 0;
+ break;
+
+ case SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2: /* ("DH2Q") */
+ rc = smb_mbc_decodef(&cce->cce_mbc,
+ "ll8.#c",
+ &op->dh_timeout, /* l */
+ &op->dh_v2_flags, /* l */
+ /* reserved */ /* 8. */
+ UUID_LEN, /* # */
+ op->create_guid); /* c */
+ if (rc != 0)
+ goto errout;
+ break;
+
+ case SMB2_CREATE_DURABLE_HANDLE_REQUEST: /* ("DHnQ") */
+ rc = smb_mbc_decodef(&cce->cce_mbc,
+ "16."); /* reserved */
+ if (rc != 0)
+ goto errout;
+ op->dh_timeout = 0; /* default */
+ op->dh_v2_flags = 0;
+ break;
}
next_cc:
@@ -708,6 +926,37 @@ smb2_encode_create_ctx(smb_request_t *sr, smb2_create_ctx_t *cc)
mbc->chain_offset - last_top);
}
+ if (cc->cc_out_flags & CCTX_DH_REQUEST) {
+ cce = &cc->cc_out_dh_request;
+
+ cce->cce_mbc.max_bytes = cce->cce_len = 8;
+ (void) smb_mbc_encodef(&cce->cce_mbc, "q", 0LL);
+
+ last_top = mbc->chain_offset;
+ rc = smb2_encode_create_ctx_elem(mbc, cce,
+ SMB2_CREATE_DURABLE_HANDLE_REQUEST);
+ if (rc)
+ return (NT_STATUS_INTERNAL_ERROR);
+ (void) smb_mbc_poke(mbc, last_top, "l",
+ mbc->chain_offset - last_top);
+ }
+
+ if (cc->cc_out_flags & CCTX_DH_REQUEST_V2) {
+ cce = &cc->cc_out_dh_request_v2;
+
+ cce->cce_mbc.max_bytes = cce->cce_len = 8;
+ (void) smb_mbc_encodef(&cce->cce_mbc, "ll",
+ op->dh_timeout, op->dh_v2_flags);
+
+ last_top = mbc->chain_offset;
+ rc = smb2_encode_create_ctx_elem(mbc, cce,
+ SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2);
+ if (rc)
+ return (NT_STATUS_INTERNAL_ERROR);
+ (void) smb_mbc_poke(mbc, last_top, "l",
+ mbc->chain_offset - last_top);
+ }
+
if (last_top >= 0)
(void) smb_mbc_poke(mbc, last_top, "l", 0);
@@ -779,4 +1028,12 @@ smb2_free_create_ctx(smb2_create_ctx_t *cc)
cce = &cc->cc_out_aapl;
MBC_FLUSH(&cce->cce_mbc);
}
+ if (cc->cc_out_flags & CCTX_DH_REQUEST) {
+ cce = &cc->cc_out_dh_request;
+ MBC_FLUSH(&cce->cce_mbc);
+ }
+ if (cc->cc_out_flags & CCTX_DH_REQUEST_V2) {
+ cce = &cc->cc_out_dh_request_v2;
+ MBC_FLUSH(&cce->cce_mbc);
+ }
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c b/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c
index 39161f7c30..b0bbbd4f40 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c
@@ -56,12 +56,12 @@ typedef struct smb2_async_req {
/*
* SMB2 header fields.
*/
+ uint64_t ar_messageid;
+ uint64_t ar_ssnid;
uint16_t ar_cmd_code;
- uint16_t ar_uid;
uint16_t ar_tid;
uint32_t ar_pid;
uint32_t ar_hdr_flags;
- uint64_t ar_messageid;
} smb2_async_req_t;
void smb2sr_do_async(smb_request_t *);
@@ -503,15 +503,15 @@ cmd_start:
NT_STATUS_INVALID_PARAMETER);
goto cmd_done;
}
- sr->smb_uid = sr->uid_user->u_uid;
+ sr->smb2_ssnid = sr->uid_user->u_ssnid;
} else {
/*
* Lookup the UID
* [MS-SMB2] 3.3.5.2 Verifying the Session
*/
ASSERT(sr->uid_user == NULL);
- sr->uid_user = smb_session_lookup_uid(session,
- sr->smb_uid);
+ sr->uid_user = smb_session_lookup_ssnid(session,
+ sr->smb2_ssnid);
if (sr->uid_user == NULL) {
smb2sr_put_error(sr,
NT_STATUS_USER_SESSION_DELETED);
@@ -873,7 +873,7 @@ smb2sr_do_async(smb_request_t *sr)
sr->smb2_messageid = ar->ar_messageid;
sr->smb_pid = ar->ar_pid;
sr->smb_tid = ar->ar_tid;
- sr->smb_uid = ar->ar_uid;
+ sr->smb2_ssnid = ar->ar_ssnid;
sr->smb2_status = 0;
/*
@@ -1050,7 +1050,7 @@ smb2sr_go_async(smb_request_t *sr,
ar->ar_messageid = sr->smb2_messageid;
ar->ar_pid = sr->smb_pid;
ar->ar_tid = sr->smb_tid;
- ar->ar_uid = sr->smb_uid;
+ ar->ar_ssnid = sr->smb2_ssnid;
sr->sr_async_req = ar;
@@ -1089,7 +1089,7 @@ smb2_decode_header(smb_request_t *sr)
if (hdr_len != SMB2_HDR_SIZE)
return (-1);
- sr->smb_uid = (uint16_t)ssnid; /* XXX wide UIDs */
+ sr->smb2_ssnid = ssnid;
if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
sr->smb2_async_id = pid |
@@ -1105,7 +1105,6 @@ smb2_decode_header(smb_request_t *sr)
int
smb2_encode_header(smb_request_t *sr, boolean_t overwrite)
{
- uint64_t ssnid = sr->smb_uid;
uint64_t pid_tid_aid; /* pid+tid, or async id */
uint32_t reply_hdr_flags;
int rc;
@@ -1131,7 +1130,7 @@ smb2_encode_header(smb_request_t *sr, boolean_t overwrite)
sr->smb2_next_reply, /* l */
sr->smb2_messageid, /* q */
pid_tid_aid, /* q */
- ssnid, /* q */
+ sr->smb2_ssnid, /* q */
sr->smb2_sig); /* 16c */
} else {
rc = smb_mbc_encodef(&sr->reply,
@@ -1145,7 +1144,7 @@ smb2_encode_header(smb_request_t *sr, boolean_t overwrite)
sr->smb2_next_reply, /* l */
sr->smb2_messageid, /* q */
pid_tid_aid, /* q */
- ssnid, /* q */
+ sr->smb2_ssnid, /* q */
sr->smb2_sig); /* 16c */
}
@@ -1265,7 +1264,8 @@ smb2sr_lookup_fid(smb_request_t *sr, smb2fid_t *fid)
sr->smb_fid = (uint16_t)fid->temporal;
sr->fid_ofile = smb_ofile_lookup_by_fid(sr, sr->smb_fid);
}
- if (sr->fid_ofile == NULL)
+ if (sr->fid_ofile == NULL ||
+ sr->fid_ofile->f_persistid != fid->persistent)
return (NT_STATUS_FILE_CLOSED);
return (0);
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_durable.c b/usr/src/uts/common/fs/smbsrv/smb2_durable.c
new file mode 100644
index 0000000000..5a253bc6d2
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_durable.c
@@ -0,0 +1,436 @@
+/*
+ * 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.
+ */
+
+/*
+ * SMB2 Durable Handle support
+ */
+
+#include <sys/types.h>
+#include <sys/cmn_err.h>
+#include <sys/fcntl.h>
+#include <sys/nbmlock.h>
+#include <smbsrv/string.h>
+#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/smb2_kproto.h>
+
+/* Windows default values from [MS-SMB2] */
+/*
+ * (times in seconds)
+ * resilient:
+ * MaxTimeout = 300 (win7+)
+ * if timeout > MaxTimeout, ERROR
+ * if timeout != 0, timeout = req.timeout
+ * if timeout == 0, timeout = (infinity) (Win7/w2k8r2)
+ * if timeout == 0, timeout = 120 (Win8+)
+ * v2:
+ * if timeout != 0, timeout = MIN(timeout, 300) (spec)
+ * if timeout != 0, timeout = timeout (win8/2k12)
+ * if timeout == 0, timeout = Share.CATimeout. \
+ * if Share.CATimeout == 0, timeout = 60 (win8/w2k12)
+ * if timeout == 0, timeout = 180 (win8.1/w2k12r2)
+ * open.timeout = 60 (win8/w2k12r2) (i.e. we ignore the request)
+ * v1:
+ * open.timeout = 16 minutes
+ */
+
+uint32_t smb2_dh_def_timeout = 60 * MILLISEC; /* mSec. */
+uint32_t smb2_dh_max_timeout = 300 * MILLISEC; /* mSec. */
+
+uint32_t smb2_res_def_timeout = 120 * MILLISEC; /* mSec. */
+uint32_t smb2_res_max_timeout = 300 * MILLISEC; /* mSec. */
+
+/*
+ * smb_dh_should_save
+ *
+ * During session tear-down, decide whether to keep a durable handle.
+ *
+ * There are two cases where we save durable handles:
+ * 1. An SMB2 LOGOFF request was received
+ * 2. An unexpected disconnect from the client
+ * Note: Specifying a PrevSessionID in session setup
+ * is considered a disconnect (we just haven't learned about it yet)
+ * In every other case, we close durable handles.
+ *
+ * [MS-SMB2] 3.3.5.6 SMB2_LOGOFF
+ * [MS-SMB2] 3.3.7.1 Handling Loss of a Connection
+ *
+ * If any of the following are true, preserve for reconnect:
+ *
+ * - Open.IsResilient is TRUE.
+ *
+ * - Open.OplockLevel == SMB2_OPLOCK_LEVEL_BATCH and
+ * Open.OplockState == Held, and Open.IsDurable is TRUE.
+ *
+ * - Open.OplockLevel == SMB2_OPLOCK_LEVEL_LEASE,
+ * Lease.LeaseState SMB2_LEASE_HANDLE_CACHING,
+ * Open.OplockState == Held, and Open.IsDurable is TRUE.
+ *
+ * - Open.IsPersistent is TRUE.
+ */
+boolean_t
+smb_dh_should_save(smb_ofile_t *of)
+{
+ ASSERT(MUTEX_HELD(&of->f_mutex));
+ ASSERT(of->dh_vers != SMB2_NOT_DURABLE);
+
+ if (of->f_user->preserve_opens == SMB2_DH_PRESERVE_NONE)
+ return (B_FALSE);
+
+ if (of->f_user->preserve_opens == SMB2_DH_PRESERVE_ALL)
+ return (B_TRUE);
+
+ if (of->dh_vers == SMB2_RESILIENT)
+ return (B_TRUE);
+
+ if (!SMB_OFILE_OPLOCK_GRANTED(of))
+ return (B_FALSE);
+
+ if (of->f_oplock_grant.og_level == SMB_OPLOCK_BATCH)
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+/*
+ * Requirements for ofile found during reconnect (MS-SMB2 3.3.5.9.7):
+ * - security descriptor must match provided descriptor
+ *
+ * If file is leased:
+ * - lease must be requested
+ * - client guid must match session guid
+ * - file name must match given name
+ * - lease key must match provided lease key
+ * If file is not leased:
+ * - Lease must not be requested
+ *
+ * dh_v2 only:
+ * - SMB2_DHANDLE_FLAG_PERSISTENT must be set if dh_persist is true
+ * - SMB2_DHANDLE_FLAG_PERSISTENT must not be set if dh_persist is false
+ * - desired access, share access, and create_options must be ignored
+ * - createguid must match
+ */
+static uint32_t
+smb2_dh_reconnect_checks(smb_request_t *sr, smb_ofile_t *of)
+{
+ smb_arg_open_t *op = &sr->sr_open;
+
+ if (op->dh_vers == SMB2_DURABLE_V2) {
+ boolean_t op_persist =
+ ((op->dh_v2_flags & SMB2_DHANDLE_FLAG_PERSISTENT) != 0);
+ if (of->dh_persist != op_persist)
+ return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ if (memcmp(op->create_guid, of->dh_create_guid, UUID_LEN))
+ return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ }
+
+ if (!smb_is_same_user(sr->user_cr, of->f_cr))
+ return (NT_STATUS_ACCESS_DENIED);
+
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * [MS-SMB2] 3.3.5.9.7 and 3.3.5.9.12 (durable reconnect v1/v2)
+ *
+ * Looks up an ofile on the server's sv_dh_list by the persistid.
+ * If found, it validates the request.
+ * (see smb2_dh_reconnect_checks() for details)
+ * If the checks are passed, add it onto the new tree's list.
+ *
+ * Note that the oplock break code path can get to an ofile via the node
+ * ofile list. It starts with a ref taken in smb_ofile_hold_olbrk, which
+ * waits if the ofile is found in state RECONNECT. That wait happens with
+ * the node ofile list lock held as reader, and the oplock mutex held.
+ * Implications of that are: While we're in state RECONNECT, we shoud NOT
+ * block (at least, not for long) and must not try to enter any of the
+ * node ofile list lock or oplock mutex. Thankfully, we don't need to
+ * enter those while reclaiming an orphaned ofile.
+ */
+uint32_t
+smb2_dh_reconnect(smb_request_t *sr)
+{
+ smb_arg_open_t *op = &sr->sr_open;
+ smb_tree_t *tree = sr->tid_tree;
+ smb_ofile_t *of;
+ cred_t *old_cr;
+ uint32_t status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ uint16_t fid = 0;
+
+ if (smb_idpool_alloc(&tree->t_fid_pool, &fid))
+ return (NT_STATUS_TOO_MANY_OPENED_FILES);
+
+ /* Find orphaned handle. */
+ of = smb_ofile_lookup_by_persistid(sr, op->dh_fileid.persistent);
+ if (of == NULL)
+ goto errout;
+
+ mutex_enter(&of->f_mutex);
+ if (of->f_state != SMB_OFILE_STATE_ORPHANED) {
+ mutex_exit(&of->f_mutex);
+ goto errout;
+ }
+
+ status = smb2_dh_reconnect_checks(sr, of);
+ if (status != NT_STATUS_SUCCESS) {
+ mutex_exit(&of->f_mutex);
+ goto errout;
+ }
+
+ /*
+ * Note: cv_broadcast(&of->f_cv) when we're
+ * done messing around in this state.
+ * See: smb_ofile_hold_olbrk()
+ */
+ of->f_state = SMB_OFILE_STATE_RECONNECT;
+ mutex_exit(&of->f_mutex);
+
+ /*
+ * At this point, we should be the only thread with a ref on the
+ * ofile, and the RECONNECT state should prevent new refs from
+ * being granted, or other durable threads from observing or
+ * reclaiming it. Put this ofile in the new tree, similar to
+ * the last part of smb_ofile_open.
+ */
+
+ old_cr = of->f_cr;
+ of->f_cr = sr->user_cr;
+ crhold(of->f_cr);
+ crfree(old_cr);
+
+ of->f_session = sr->session; /* hold is via user and tree */
+ smb_user_hold_internal(sr->uid_user);
+ of->f_user = sr->uid_user;
+ smb_tree_hold_internal(tree);
+ of->f_tree = tree;
+ of->f_fid = fid;
+
+ op->op_oplock_level = of->f_oplock_grant.og_level;
+
+ smb_llist_enter(&tree->t_ofile_list, RW_WRITER);
+ smb_llist_insert_tail(&tree->t_ofile_list, of);
+ smb_llist_exit(&tree->t_ofile_list);
+ atomic_inc_32(&tree->t_open_files);
+ atomic_inc_32(&sr->session->s_file_cnt);
+
+ /*
+ * The ofile is now in the caller's session & tree.
+ *
+ * In case smb_ofile_hold or smb_oplock_send_brk() are
+ * waiting for state RECONNECT to complete, wakeup.
+ */
+ mutex_enter(&of->f_mutex);
+ of->dh_expire_time = 0;
+ of->f_state = SMB_OFILE_STATE_OPEN;
+ cv_broadcast(&of->f_cv);
+ mutex_exit(&of->f_mutex);
+
+ /*
+ * The ofile is now visible in the new session.
+ * From here, this is similar to the last part of
+ * smb_common_open().
+ */
+ op->fqi.fq_fattr.sa_mask = SMB_AT_ALL;
+ (void) smb_node_getattr(sr, of->f_node, zone_kcred(), of,
+ &op->fqi.fq_fattr);
+
+ /*
+ * Set up the fileid and dosattr in open_param for response
+ */
+ op->fileid = op->fqi.fq_fattr.sa_vattr.va_nodeid;
+ op->dattr = op->fqi.fq_fattr.sa_dosattr;
+
+ /*
+ * Set up the file type in open_param for the response
+ * The ref. from ofile lookup is "given" to fid_ofile.
+ */
+ op->ftype = SMB_FTYPE_DISK;
+ sr->smb_fid = of->f_fid;
+ sr->fid_ofile = of;
+
+ if (smb_node_is_file(of->f_node)) {
+ op->dsize = op->fqi.fq_fattr.sa_vattr.va_size;
+ } else {
+ /* directory or symlink */
+ op->dsize = 0;
+ }
+
+ op->create_options = 0; /* no more modifications wanted */
+ op->action_taken = SMB_OACT_OPENED;
+ return (NT_STATUS_SUCCESS);
+
+errout:
+ if (of != NULL)
+ smb_ofile_release(of);
+ if (fid != 0)
+ smb_idpool_free(&tree->t_fid_pool, fid);
+
+ return (status);
+}
+
+/*
+ * Durable handle expiration
+ * ofile state is _EXPIRED
+ */
+static void
+smb2_dh_expire(void *arg)
+{
+ smb_ofile_t *of = (smb_ofile_t *)arg;
+
+ smb_ofile_close(of, 0);
+ smb_ofile_release(of);
+}
+
+void
+smb2_durable_timers(smb_server_t *sv)
+{
+ smb_hash_t *hash;
+ smb_llist_t *bucket;
+ smb_ofile_t *of;
+ hrtime_t now;
+ int i;
+
+ hash = sv->sv_persistid_ht;
+ now = gethrtime();
+
+ for (i = 0; i < hash->num_buckets; i++) {
+ bucket = &hash->buckets[i].b_list;
+ smb_llist_enter(bucket, RW_READER);
+ for (of = smb_llist_head(bucket);
+ of != NULL;
+ of = smb_llist_next(bucket, of)) {
+ SMB_OFILE_VALID(of);
+
+ /*
+ * Check outside the mutex first to avoid some
+ * mutex_enter work in this loop. If the state
+ * changes under foot, the worst that happens
+ * is we either enter the mutex when we might
+ * not have needed to, or we miss some DH in
+ * this pass and get it on the next.
+ */
+ if (of->f_state != SMB_OFILE_STATE_ORPHANED)
+ continue;
+
+ mutex_enter(&of->f_mutex);
+ /* STATE_ORPHANED implies dh_expire_time != 0 */
+ if (of->f_state == SMB_OFILE_STATE_ORPHANED &&
+ of->dh_expire_time <= now) {
+ of->f_state = SMB_OFILE_STATE_EXPIRED;
+ /* inline smb_ofile_hold_internal() */
+ of->f_refcnt++;
+ smb_llist_post(bucket, of, smb2_dh_expire);
+ }
+ mutex_exit(&of->f_mutex);
+ }
+ smb_llist_exit(bucket);
+ }
+}
+
+/*
+ * Clean out durable handles during shutdown.
+ * Like, smb2_durable_timers but expire all,
+ * and make sure the hash buckets are empty.
+ */
+void
+smb2_dh_shutdown(smb_server_t *sv)
+{
+ smb_hash_t *hash;
+ smb_llist_t *bucket;
+ smb_ofile_t *of;
+ int i;
+
+ hash = sv->sv_persistid_ht;
+
+ for (i = 0; i < hash->num_buckets; i++) {
+ bucket = &hash->buckets[i].b_list;
+ smb_llist_enter(bucket, RW_READER);
+ of = smb_llist_head(bucket);
+ while (of != NULL) {
+ SMB_OFILE_VALID(of);
+ mutex_enter(&of->f_mutex);
+
+ switch (of->f_state) {
+ case SMB_OFILE_STATE_ORPHANED:
+ of->f_state = SMB_OFILE_STATE_EXPIRED;
+ /* inline smb_ofile_hold_internal() */
+ of->f_refcnt++;
+ smb_llist_post(bucket, of, smb2_dh_expire);
+ break;
+ default:
+ break;
+ }
+ mutex_exit(&of->f_mutex);
+ of = smb_llist_next(bucket, of);
+ }
+ smb_llist_exit(bucket);
+ }
+
+#ifdef DEBUG
+ for (i = 0; i < hash->num_buckets; i++) {
+ bucket = &hash->buckets[i].b_list;
+ smb_llist_enter(bucket, RW_READER);
+ of = smb_llist_head(bucket);
+ while (of != NULL) {
+ SMB_OFILE_VALID(of);
+ cmn_err(CE_NOTE, "dh_shutdown leaked of=%p",
+ (void *)of);
+ of = smb_llist_next(bucket, of);
+ }
+ smb_llist_exit(bucket);
+ }
+#endif // DEBUG
+}
+
+uint32_t
+smb2_fsctl_resiliency(smb_request_t *sr, smb_fsctl_t *fsctl)
+{
+ uint32_t timeout;
+ smb_ofile_t *of = sr->fid_ofile;
+
+ /*
+ * Note: The spec does not explicitly prohibit resilient directories
+ * the same way it prohibits durable directories. We prohibit them
+ * anyway as a simplifying assumption, as there doesn't seem to be
+ * much use for it. (HYPER-V only seems to use it on files anyway)
+ */
+ if (fsctl->InputCount < 8 || !smb_node_is_file(of->f_node))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ (void) smb_mbc_decodef(fsctl->in_mbc, "l4.",
+ &timeout); /* milliseconds */
+
+ if (smb2_enable_dh == 0)
+ return (NT_STATUS_NOT_SUPPORTED);
+
+ /*
+ * The spec wants us to return INVALID_PARAMETER if the timeout
+ * is too large, but we have no way of informing the client
+ * what an appropriate timeout is, so just set the timeout to
+ * our max and return SUCCESS.
+ */
+ if (timeout == 0)
+ timeout = smb2_res_def_timeout;
+ if (timeout > smb2_res_max_timeout)
+ timeout = smb2_res_max_timeout;
+
+ mutex_enter(&of->f_mutex);
+ of->dh_vers = SMB2_RESILIENT;
+ of->dh_timeout_offset = MSEC2NSEC(timeout);
+ mutex_exit(&of->f_mutex);
+
+ return (NT_STATUS_SUCCESS);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_ioctl.c b/usr/src/uts/common/fs/smbsrv/smb2_ioctl.c
index 95bb5a68b9..df0881f31b 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_ioctl.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_ioctl.c
@@ -275,8 +275,7 @@ smb2_ioc_tbl[] = {
{ FSCTL_SRV_COPYCHUNK_WRITE, 0, smb2_fsctl_notsup },
{ FSCTL_SRV_READ_HASH, 0, smb2_fsctl_notsup },
- { FSCTL_LMR_REQUEST_RESILIENCY,
- ITF_NO_FID, smb2_fsctl_notsup },
+ { FSCTL_LMR_REQUEST_RESILIENCY, 0, smb2_fsctl_resiliency },
{ FSCTL_QUERY_NETWORK_INTERFACE_INFO,
ITF_NO_FID, smb2_fsctl_notsup },
{ FSCTL_VALIDATE_NEGOTIATE_INFO,
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_logoff.c b/usr/src/uts/common/fs/smbsrv/smb2_logoff.c
index 772c8f6e46..497e950126 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_logoff.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_logoff.c
@@ -43,6 +43,7 @@ smb2_logoff(smb_request_t *sr)
DTRACE_SMB2_START(op__Logoff, smb_request_t *, sr);
+ sr->uid_user->preserve_opens = SMB2_DH_PRESERVE_ALL;
smb_user_logoff(sr->uid_user);
DTRACE_SMB2_DONE(op__Logoff, smb_request_t *, sr);
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_oplock.c b/usr/src/uts/common/fs/smbsrv/smb2_oplock.c
index ef7ad73d8f..2736435bdc 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_oplock.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_oplock.c
@@ -135,9 +135,9 @@ smb2_oplock_break_notification(smb_request_t *sr, uint8_t brk)
*/
sr->smb2_cmd_code = SMB2_OPLOCK_BREAK;
sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR;
- sr->smb_tid = ofile->f_tree->t_tid;
+ sr->smb_tid = 0;
sr->smb_pid = 0;
- sr->smb_uid = 0;
+ sr->smb2_ssnid = 0;
sr->smb2_messageid = UINT64_MAX;
(void) smb2_encode_header(sr, B_FALSE);
@@ -145,7 +145,7 @@ smb2_oplock_break_notification(smb_request_t *sr, uint8_t brk)
* SMB2 Oplock Break, variable part
*/
StructSize = 24;
- smb2fid.persistent = 0;
+ smb2fid.persistent = ofile->f_persistid;
smb2fid.temporal = ofile->f_fid;
(void) smb_mbc_encodef(
&sr->reply, "wb5.qq",
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_session_setup.c b/usr/src/uts/common/fs/smbsrv/smb2_session_setup.c
index ba89cc1603..4de814f378 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_session_setup.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_session_setup.c
@@ -37,7 +37,7 @@ smb2_session_setup(smb_request_t *sr)
uint32_t Channel;
uint16_t SecBufOffset;
uint16_t SecBufLength;
- uint64_t PrevSessionId;
+ uint64_t PrevSsnId;
uint16_t SessionFlags;
uint32_t status;
int skip;
@@ -55,7 +55,7 @@ smb2_session_setup(smb_request_t *sr)
&Channel, /* l */
&SecBufOffset, /* w */
&SecBufLength, /* w */
- &PrevSessionId); /* q */
+ &PrevSsnId); /* q */
if (rc)
return (SDRC_ERROR);
@@ -116,6 +116,19 @@ smb2_session_setup(smb_request_t *sr)
if (sr->uid_user->u_flags & SMB_USER_FLAG_ANON)
SessionFlags |= SMB2_SESSION_FLAG_IS_NULL;
smb2_ss_adjust_credits(sr);
+
+ /*
+ * PrevSsnId is a session that the client is reporting as
+ * having gone away, and for which we might not yet have seen
+ * a disconnect. We need to log off the previous session so
+ * any durable handles in that session will become orphans
+ * that can be reclaimed in this new session. Note that
+ * either zero or the _current_ session ID means there is
+ * no previous session to logoff.
+ */
+ if (PrevSsnId != 0 &&
+ PrevSsnId != sr->smb2_ssnid)
+ smb_server_logoff_ssnid(sr, PrevSsnId);
break;
/*
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_signing.c b/usr/src/uts/common/fs/smbsrv/smb2_signing.c
index e07fb99e4b..ed19e6950e 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_signing.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_signing.c
@@ -366,7 +366,7 @@ smb2_sign_check_request(smb_request_t *sr)
* Don't check commands with a zero session ID.
* [MS-SMB2] 3.3.4.1.1
*/
- if (sr->smb_uid == 0 || u == NULL)
+ if (sr->smb2_ssnid == 0 || u == NULL)
return (0);
/* In case _sign_begin failed. */
diff --git a/usr/src/uts/common/fs/smbsrv/smb_authenticate.c b/usr/src/uts/common/fs/smbsrv/smb_authenticate.c
index e4d72305d6..72d06d4d33 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_authenticate.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_authenticate.c
@@ -51,6 +51,7 @@ static uint32_t smb_priv_xlate(smb_token_t *);
/*
* Handle old-style session setup (non-extended security)
+ * Note: Used only by SMB1
*
* The user information is passed to smbd for authentication.
* If smbd can authenticate the user an access token is returned and we
@@ -69,6 +70,7 @@ smb_authenticate_old(smb_request_t *sr)
/* user cleanup in smb_request_free */
sr->uid_user = user;
sr->smb_uid = user->u_uid;
+ sr->smb2_ssnid = 0;
/*
* Open a connection to the local logon service.
@@ -231,20 +233,42 @@ smb_authenticate_ext(smb_request_t *sr)
ASSERT(sr->uid_user == NULL);
/*
- * On the first request (UID==0) create a USER object.
- * On subsequent requests (UID!=0) find the USER object.
+ * Paranoid: While finding/creating the user object, make sure
+ * SMB2 ignores smb_uid, and SMB1 ignores smb2_ssnid. The
+ * logic below assumes the "other" one is always zero; both
+ * the "first request" tests and smb_session_lookup_uid_st.
+ */
+ if (sr->session->dialect >= SMB_VERS_2_BASE) {
+ /* SMB2+ ignores smb_uid */
+ ASSERT(sr->smb_uid == 0);
+ sr->smb_uid = 0;
+ } else {
+ /* SMB1 ignores smb2_ssnid */
+ ASSERT(sr->smb2_ssnid == 0);
+ sr->smb2_ssnid = 0;
+ }
+
+ /*
+ * On the first request (UID/ssnid==0) create a USER object.
+ * On subsequent requests (UID/ssnid!=0) find the USER object.
* Either way, sr->uid_user is set, so our ref. on the
* user object is dropped during normal cleanup work
* for the smb_request (sr). Ditto u_authsock.
*/
- if (sr->smb_uid == 0) {
+ if (sr->smb2_ssnid == 0 && sr->smb_uid == 0) {
user = smb_user_new(sr->session);
if (user == NULL)
return (NT_STATUS_TOO_MANY_SESSIONS);
/* user cleanup in smb_request_free */
sr->uid_user = user;
- sr->smb_uid = user->u_uid;
+ if (sr->session->dialect >= SMB_VERS_2_BASE) {
+ /* Intentionally leave smb_uid=0 for SMB2 */
+ sr->smb2_ssnid = user->u_ssnid;
+ } else {
+ /* Intentionally leave smb2_ssnid=0 for SMB1 */
+ sr->smb_uid = user->u_uid;
+ }
/*
* Open a connection to the local logon service.
@@ -263,7 +287,7 @@ smb_authenticate_ext(smb_request_t *sr)
msg_hdr.lmh_msgtype = LSA_MTYPE_ESFIRST;
} else {
user = smb_session_lookup_uid_st(sr->session,
- sr->smb_uid, SMB_USER_STATE_LOGGING_ON);
+ sr->smb2_ssnid, sr->smb_uid, SMB_USER_STATE_LOGGING_ON);
if (user == NULL)
return (NT_STATUS_USER_SESSION_DELETED);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_kutil.c b/usr/src/uts/common/fs/smbsrv/smb_kutil.c
index 5c74e4f8cd..c5be4710d8 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_kutil.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_kutil.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/param.h>
@@ -40,6 +40,7 @@
#include <sys/sid.h>
#include <sys/priv_names.h>
+#include <sys/bitmap.h>
static kmem_cache_t *smb_dtor_cache = NULL;
@@ -332,8 +333,22 @@ smb_idpool_alloc(
pool->id_pool[pool->id_idx] |= bit;
*id = (uint16_t)(pool->id_idx * 8 + (uint32_t)bit_idx);
pool->id_free_counter--;
- pool->id_bit = bit;
- pool->id_bit_idx = bit_idx;
+ /*
+ * Leave position at next bit to allocate,
+ * so we don't keep re-using the last in an
+ * alloc/free/alloc/free sequence. Doing
+ * that can confuse some SMB clients.
+ */
+ if (bit & 0x80) {
+ pool->id_bit = 1;
+ pool->id_bit_idx = 0;
+ pool->id_idx++;
+ pool->id_idx &= pool->id_idx_msk;
+ } else {
+ pool->id_bit = (bit << 1);
+ pool->id_bit_idx = bit_idx + 1;
+ /* keep id_idx */
+ }
mutex_exit(&pool->id_mutex);
return (0);
}
@@ -445,6 +460,16 @@ smb_llist_destructor(
}
/*
+ * smb_llist_enter
+ * Not a macro so dtrace smbsrv:* can see it.
+ */
+void
+smb_llist_enter(smb_llist_t *ll, krw_t mode)
+{
+ rw_enter(&ll->ll_lock, mode);
+}
+
+/*
* Post an object to the delete queue. The delete queue will be processed
* during list exit or list destruction. Objects are often posted for
* deletion during list iteration (while the list is locked) but that is
@@ -636,6 +661,16 @@ smb_slist_destructor(
}
/*
+ * smb_slist_enter
+ * Not a macro so dtrace smbsrv:* can see it.
+ */
+void
+smb_slist_enter(smb_slist_t *sl)
+{
+ mutex_enter(&(sl)->sl_mutex);
+}
+
+/*
* smb_slist_insert_head
*
* This function inserts the object passed a the beginning of the list.
@@ -799,90 +834,46 @@ smb_rwx_destroy(
}
/*
- * smb_rwx_rwexit
+ * smb_rwx_rwenter
*/
void
-smb_rwx_rwexit(
- smb_rwx_t *rwx)
+smb_rwx_rwenter(smb_rwx_t *rwx, krw_t mode)
{
- if (rw_write_held(&rwx->rwx_lock)) {
- ASSERT(rw_owner(&rwx->rwx_lock) == curthread);
- mutex_enter(&rwx->rwx_mutex);
- if (rwx->rwx_waiting) {
- rwx->rwx_waiting = B_FALSE;
- cv_broadcast(&rwx->rwx_cv);
- }
- mutex_exit(&rwx->rwx_mutex);
- }
- rw_exit(&rwx->rwx_lock);
+ rw_enter(&rwx->rwx_lock, mode);
}
/*
- * smb_rwx_rwupgrade
+ * smb_rwx_rwexit
*/
-krw_t
-smb_rwx_rwupgrade(
+void
+smb_rwx_rwexit(
smb_rwx_t *rwx)
{
- if (rw_write_held(&rwx->rwx_lock)) {
- ASSERT(rw_owner(&rwx->rwx_lock) == curthread);
- return (RW_WRITER);
- }
- if (!rw_tryupgrade(&rwx->rwx_lock)) {
- rw_exit(&rwx->rwx_lock);
- rw_enter(&rwx->rwx_lock, RW_WRITER);
- }
- return (RW_READER);
+ rw_exit(&rwx->rwx_lock);
}
-/*
- * smb_rwx_rwrestore
- */
-void
-smb_rwx_rwdowngrade(
- smb_rwx_t *rwx,
- krw_t mode)
-{
- ASSERT(rw_write_held(&rwx->rwx_lock));
- ASSERT(rw_owner(&rwx->rwx_lock) == curthread);
-
- if (mode == RW_WRITER) {
- return;
- }
- ASSERT(mode == RW_READER);
- mutex_enter(&rwx->rwx_mutex);
- if (rwx->rwx_waiting) {
- rwx->rwx_waiting = B_FALSE;
- cv_broadcast(&rwx->rwx_cv);
- }
- mutex_exit(&rwx->rwx_mutex);
- rw_downgrade(&rwx->rwx_lock);
-}
/*
- * smb_rwx_wait
+ * smb_rwx_cvwait
*
- * This function assumes the smb_rwx lock was enter in RW_READER or RW_WRITER
+ * Wait on rwx->rw_cv, dropping the rw lock and retake after wakeup.
+ * Assumes the smb_rwx lock was entered in RW_READER or RW_WRITER
* mode. It will:
*
* 1) release the lock and save its current mode.
- * 2) wait until the condition variable is signaled. This can happen for
- * 2 reasons: When a writer releases the lock or when the time out (if
- * provided) expires.
+ * 2) wait until the condition variable is signaled.
* 3) re-acquire the lock in the mode saved in (1).
+ *
+ * Lock order: rwlock, mutex
*/
int
-smb_rwx_rwwait(
+smb_rwx_cvwait(
smb_rwx_t *rwx,
clock_t timeout)
{
krw_t mode;
int rc = 1;
- mutex_enter(&rwx->rwx_mutex);
- rwx->rwx_waiting = B_TRUE;
- mutex_exit(&rwx->rwx_mutex);
-
if (rw_write_held(&rwx->rwx_lock)) {
ASSERT(rw_owner(&rwx->rwx_lock) == curthread);
mode = RW_WRITER;
@@ -890,16 +881,16 @@ smb_rwx_rwwait(
ASSERT(rw_read_held(&rwx->rwx_lock));
mode = RW_READER;
}
- rw_exit(&rwx->rwx_lock);
mutex_enter(&rwx->rwx_mutex);
- if (rwx->rwx_waiting) {
- if (timeout == -1) {
- cv_wait(&rwx->rwx_cv, &rwx->rwx_mutex);
- } else {
- rc = cv_reltimedwait(&rwx->rwx_cv, &rwx->rwx_mutex,
- timeout, TR_CLOCK_TICK);
- }
+ rw_exit(&rwx->rwx_lock);
+
+ rwx->rwx_waiting = B_TRUE;
+ if (timeout == -1) {
+ cv_wait(&rwx->rwx_cv, &rwx->rwx_mutex);
+ } else {
+ rc = cv_reltimedwait(&rwx->rwx_cv, &rwx->rwx_mutex,
+ timeout, TR_CLOCK_TICK);
}
mutex_exit(&rwx->rwx_mutex);
@@ -907,6 +898,24 @@ smb_rwx_rwwait(
return (rc);
}
+/*
+ * smb_rwx_cvbcast
+ *
+ * Wake up threads waiting on rx_cv
+ * The rw lock may or may not be held.
+ * The mutex MUST NOT be held.
+ */
+void
+smb_rwx_cvbcast(smb_rwx_t *rwx)
+{
+ mutex_enter(&rwx->rwx_mutex);
+ if (rwx->rwx_waiting) {
+ rwx->rwx_waiting = B_FALSE;
+ cv_broadcast(&rwx->rwx_cv);
+ }
+ mutex_exit(&rwx->rwx_mutex);
+}
+
/* smb_idmap_... moved to smb_idmap.c */
uint64_t
@@ -1269,8 +1278,8 @@ smb_avl_destroy(smb_avl_t *avl)
*
* Returns:
*
- * ENOTACTIVE AVL is not in READY state
- * EEXIST The item is already in AVL
+ * ENOTACTIVE AVL is not in READY state
+ * EEXIST The item is already in AVL
*/
int
smb_avl_add(smb_avl_t *avl, void *item)
@@ -1737,3 +1746,51 @@ smb_threshold_wake_all(smb_cmd_threshold_t *ct)
cv_broadcast(&ct->ct_cond);
mutex_exit(&ct->ct_mutex);
}
+
+/* taken from mod_hash_byptr */
+uint_t
+smb_hash_uint64(smb_hash_t *hash, uint64_t val)
+{
+ uint64_t k = val >> hash->rshift;
+ uint_t idx = ((uint_t)k) & (hash->num_buckets - 1);
+
+ return (idx);
+}
+
+boolean_t
+smb_is_pow2(size_t n)
+{
+ return ((n & (n - 1)) == 0);
+}
+
+smb_hash_t *
+smb_hash_create(size_t elemsz, size_t link_offset,
+ uint32_t num_buckets)
+{
+ smb_hash_t *hash = kmem_alloc(sizeof (*hash), KM_SLEEP);
+ int i;
+
+ if (!smb_is_pow2(num_buckets))
+ num_buckets = 1 << highbit(num_buckets);
+
+ hash->rshift = highbit(elemsz);
+ hash->num_buckets = num_buckets;
+ hash->buckets = kmem_zalloc(num_buckets * sizeof (smb_bucket_t),
+ KM_SLEEP);
+ for (i = 0; i < num_buckets; i++)
+ smb_llist_constructor(&hash->buckets[i].b_list, elemsz,
+ link_offset);
+ return (hash);
+}
+
+void
+smb_hash_destroy(smb_hash_t *hash)
+{
+ int i;
+
+ for (i = 0; i < hash->num_buckets; i++)
+ smb_llist_destructor(&hash->buckets[i].b_list);
+
+ kmem_free(hash->buckets, hash->num_buckets * sizeof (smb_bucket_t));
+ kmem_free(hash, sizeof (*hash));
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_lock.c b/usr/src/uts/common/fs/smbsrv/smb_lock.c
index 6afe9396a9..e12e3be6fd 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_lock.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_lock.c
@@ -415,6 +415,7 @@ smb_lock_range_access(
void
smb_node_destroy_lock_by_ofile(smb_node_t *node, smb_ofile_t *file)
{
+ cred_t *kcr = zone_kcred();
smb_lock_t *lock;
smb_lock_t *nxtl;
list_t destroy_list;
@@ -457,7 +458,7 @@ smb_node_destroy_lock_by_ofile(smb_node_t *node, smb_ofile_t *file)
nxtl = smb_llist_next(&node->n_lock_list, lock);
if (lock->l_file == file) {
smb_llist_remove(&node->n_lock_list, lock);
- smb_lock_posix_unlock(node, lock, file->f_user->u_cred);
+ smb_lock_posix_unlock(node, lock, kcr);
list_insert_tail(&destroy_list, lock);
}
lock = nxtl;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_node.c b/usr/src/uts/common/fs/smbsrv/smb_node.c
index e19d4e668b..66eabe3fe2 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_node.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_node.c
@@ -189,6 +189,7 @@ smb_node_fini(void)
#ifdef DEBUG
for (i = 0; i <= SMBND_HASH_MASK; i++) {
+ smb_llist_t *bucket;
smb_node_t *node;
/*
@@ -204,8 +205,13 @@ smb_node_fini(void)
* smb_node_lookup() and smb_node_release(). You must track that
* down.
*/
- node = smb_llist_head(&smb_node_hash_table[i]);
- ASSERT(node == NULL);
+ bucket = &smb_node_hash_table[i];
+ node = smb_llist_head(bucket);
+ while (node != NULL) {
+ cmn_err(CE_NOTE, "leaked node: 0x%p %s",
+ (void *)node, node->od_name);
+ node = smb_llist_next(bucket, node);
+ }
}
#endif
@@ -1227,7 +1233,7 @@ smb_node_constructor(void *buf, void *un, int kmflags)
bzero(node, sizeof (smb_node_t));
smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
- offsetof(smb_ofile_t, f_nnd));
+ offsetof(smb_ofile_t, f_node_lnd));
smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
offsetof(smb_lock_t, l_lnd));
smb_llist_constructor(&node->n_wlock_list, sizeof (smb_lock_t),
diff --git a/usr/src/uts/common/fs/smbsrv/smb_odir.c b/usr/src/uts/common/fs/smbsrv/smb_odir.c
index 54fa075dbe..55a6c38dd1 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_odir.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_odir.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -264,6 +264,7 @@ static int smb_odir_next_odirent(smb_odir_t *, smb_odirent_t *);
static boolean_t smb_odir_lookup_link(smb_request_t *, smb_odir_t *,
char *, smb_node_t **);
static boolean_t smb_odir_match_name(smb_odir_t *, smb_odirent_t *);
+static void smb_odir_delete(void *);
/*
@@ -282,7 +283,7 @@ smb_odir_openpath(smb_request_t *sr, char *path, uint16_t sattr,
smb_tree_t *tree;
smb_node_t *dnode;
char pattern[MAXNAMELEN];
- uint16_t odid;
+ uint16_t odid;
cred_t *cr;
ASSERT(sr);
@@ -445,6 +446,8 @@ smb_odir_hold(smb_odir_t *od)
void
smb_odir_release(smb_odir_t *od)
{
+ smb_tree_t *tree = od->d_tree;
+
SMB_ODIR_VALID(od);
mutex_enter(&od->d_mutex);
@@ -462,7 +465,8 @@ smb_odir_release(smb_odir_t *od)
od->d_refcnt--;
if (od->d_refcnt == 0) {
od->d_state = SMB_ODIR_STATE_CLOSED;
- smb_tree_post_odir(od->d_tree, od);
+ smb_llist_post(&tree->t_odir_list, od,
+ smb_odir_delete);
}
break;
case SMB_ODIR_STATE_CLOSED:
@@ -989,7 +993,7 @@ smb_odir_reopen(smb_odir_t *od, const char *pattern, uint16_t sattr)
* Remove the odir from the tree list before freeing resources
* associated with the odir.
*/
-void
+static void
smb_odir_delete(void *arg)
{
smb_tree_t *tree;
@@ -1007,6 +1011,13 @@ smb_odir_delete(void *arg)
atomic_dec_32(&tree->t_session->s_dir_cnt);
smb_llist_exit(&tree->t_odir_list);
+ /*
+ * This odir is no longer on t_odir_list, however...
+ *
+ * This is called via smb_llist_post, which means it may run
+ * BEFORE smb_odir_release drops d_mutex (if another thread
+ * flushes the delete queue before we do). Synchronize.
+ */
mutex_enter(&od->d_mutex);
mutex_exit(&od->d_mutex);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_ofile.c b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
index 445ef9f040..e7b07732d1 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_ofile.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
@@ -22,7 +22,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2016 Syneto S.R.L. All rights reserved.
* Copyright (c) 2016 by Delphix. All rights reserved.
- * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -71,22 +71,32 @@
* ------------------
*
* +-------------------------+ T0
- * | SMB_OFILE_STATE_OPEN |<----------- Creation/Allocation
+ * | SMB_OFILE_STATE_OPEN |<--+-------- Creation/Allocation
+ * +-------------------------+ |
+ * | | | T5
+ * | | +---------------------------+
+ * | | | SMB_OFILE_STATE_RECONNECT |
+ * | | +---------------------------+
+ * | | ^
+ * | v |
+ * | +---------------+ |
+ * | | STATE_SAVE_DH | |
+ * | | STATE_SAVING | |
+ * | +---------------+ |
+ * | | | T4
+ * | T1 | T3 +--------------------------+
+ * | +------>| SMB_OFILE_STATE_ORPHANED |
+ * v +--------------------------+
+ * +-------------------------+ | |
+ * | SMB_OFILE_STATE_CLOSING |<--+ T6 | T7
+ * +-------------------------+ |
+ * | ^ v
+ * | T2 | T8 +-------------------------+
+ * | +-------| SMB_OFILE_STATE_EXPIRED |
+ * v +-------------------------+
* +-------------------------+
- * |
- * | T1
- * |
- * v
- * +-------------------------+
- * | SMB_OFILE_STATE_CLOSING |
- * +-------------------------+
- * |
- * | T2
- * |
- * v
- * +-------------------------+ T3
* | SMB_OFILE_STATE_CLOSED |----------> Deletion/Free
- * +-------------------------+
+ * +-------------------------+ T9
*
* SMB_OFILE_STATE_OPEN
*
@@ -94,8 +104,34 @@
* - The ofile is queued in the list of ofiles of its tree.
* - References will be given out if the ofile is looked up.
*
+ * SMB_OFILE_STATE_SAVE_DH
+ *
+ * Similar to state _CLOSING, but instead of deleting the ofile,
+ * it leaves the ofile in state _ORPHANED (for later reclaim).
+ * Will move to _SAVING after last ref, then _ORPHANED.
+ *
+ * While in this state:
+ * - The ofile has been marked for preservation during a
+ * walk of the tree ofile list to close multiple files.
+ * - References will not be given out if the ofile is looked up,
+ * except for oplock break processing.
+ * - Still affects Sharing Violation rules
+ *
+ * SMB_OFILE_STATE_SAVING
+ *
+ * Transient state used to keep oplock break processing out
+ * while the ofile moves to state _ORPHANED.
+ *
+ * While in this state:
+ * - References will not be given out if the ofile is looked up,
+ * except for oplock break processing.
+ * - Still affects Sharing Violation rules
+ *
* SMB_OFILE_STATE_CLOSING
*
+ * Close has been requested. Stay in this state until the last
+ * ref. is gone, then move to state _CLOSED
+ *
* While in this state:
* - The ofile is queued in the list of ofiles of its tree.
* - References will not be given out if the ofile is looked up.
@@ -109,6 +145,38 @@
* - References will not be given out if the ofile is looked up.
* - The resources associated with the ofile remain.
*
+ * SMB_OFILE_STATE_ORPHANED
+ *
+ * While in this state:
+ * - The ofile is queued in the list of ofiles of its tree.
+ * - Can be reclaimed by the original owner
+ * - References will not be given out if the ofile is looked up.
+ * - All the tree, user, and session "up" pointers are NULL!
+ * - Will eventually be "expired" if not reclaimed
+ * - Can be closed if its oplock is broken
+ * - Still affects Sharing Violation rules
+ *
+ * SMB_OFILE_STATE_EXPIRED
+ *
+ * While in this state:
+ * - The ofile is queued in the list of ofiles of its tree.
+ * - References will not be given out if the ofile is looked up.
+ * - The ofile has not been reclaimed and will soon be closed,
+ * due to, for example, the durable handle timer expiring, or its
+ * oplock being broken.
+ * - Cannot be reclaimed at this point
+ *
+ * SMB_OFILE_STATE_RECONNECT
+ *
+ * Transient state used to keep oplock break processing out
+ * while the ofile moves from state _ORPHANED to _OPEN.
+ *
+ * While in this state:
+ * - The ofile is being reclaimed; do not touch it.
+ * - References will not be given out if the ofile is looked up.
+ * - Still affects Sharing Violation rules
+ * - see smb2_dh_reconnect() for which members need to be avoided
+ *
* Transition T0
*
* This transition occurs in smb_ofile_open(). A new ofile is created and
@@ -116,7 +184,9 @@
*
* Transition T1
*
- * This transition occurs in smb_ofile_close().
+ * This transition occurs in smb_ofile_close(). Note that this only happens
+ * when we determine that an ofile should be closed in spite of its durable
+ * handle properties.
*
* Transition T2
*
@@ -125,6 +195,50 @@
* transition to occur, the ofile must be in the SMB_OFILE_STATE_CLOSED
* state and the reference count be zero.
*
+ * Transition T3
+ *
+ * This transition occurs in smb_ofile_orphan_dh(). It happens during an
+ * smb2 logoff, or during a session disconnect when certain conditions are
+ * met. The ofile and structures above it will be kept around until the ofile
+ * either gets reclaimed, expires after f_timeout_offset nanoseconds, or its
+ * oplock is broken.
+ *
+ * Transition T4
+ *
+ * This transition occurs in smb2_dh_reconnect(). An smb2 create request
+ * with a DURABLE_HANDLE_RECONNECT(_V2) create context has been
+ * recieved from the original owner. If leases are supported or it's
+ * RECONNECT_V2, reconnect is subject to additional conditions. The ofile
+ * will be unwired from the old, disconnected session, tree, and user,
+ * and wired up to its new context.
+ *
+ * Transition T5
+ *
+ * This transition occurs in smb2_dh_reconnect(). The ofile has been
+ * successfully reclaimed.
+ *
+ * Transition T6
+ *
+ * This transition occurs in smb_ofile_close(). The ofile has been orphaned
+ * while some thread was blocked, and that thread closes the ofile. Can only
+ * happen when the ofile is orphaned due to an SMB2 LOGOFF request.
+ *
+ * Transition T7
+ *
+ * This transition occurs in smb_session_durable_timers() and
+ * smb_oplock_sched_async_break(). The ofile will soon be closed.
+ * In the former case, f_timeout_offset nanoseconds have passed since
+ * the ofile was orphaned. In the latter, an oplock break occured
+ * on the ofile while it was orphaned.
+ *
+ * Transition T8
+ *
+ * This transition occurs in smb_ofile_close().
+ *
+ * Transition T9
+ *
+ * This transition occurs in smb_ofile_delete().
+ *
* Comments
* --------
*
@@ -142,7 +256,8 @@
* Rules of access to a ofile structure:
*
* 1) In order to avoid deadlocks, when both (mutex and lock of the ofile
- * list) have to be entered, the lock must be entered first.
+ * list) have to be entered, the lock must be entered first. Additionally,
+ * f_mutex must not be held when removing the ofile from sv_persistid_ht.
*
* 2) All actions applied to an ofile require a reference count.
*
@@ -162,11 +277,18 @@
* being queued in that list is NOT registered by incrementing the
* reference count.
*/
-#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb2_kproto.h>
#include <smbsrv/smb_fsops.h>
+#include <sys/time.h>
+
+/* Don't leak object addresses */
+#define SMB_OFILE_PERSISTID(of) \
+ ((uintptr_t)&smb_cache_ofile ^ (uintptr_t)(of))
static boolean_t smb_ofile_is_open_locked(smb_ofile_t *);
-static smb_ofile_t *smb_ofile_close_and_next(smb_ofile_t *);
+static void smb_ofile_delete(void *arg);
+static void smb_ofile_save_dh(void *arg);
+
static int smb_ofile_netinfo_encode(smb_ofile_t *, uint8_t *, size_t,
uint32_t *);
static int smb_ofile_netinfo_init(smb_ofile_t *, smb_netfileinfo_t *);
@@ -207,7 +329,9 @@ smb_ofile_open(
of->f_state = SMB_OFILE_STATE_OPEN;
of->f_refcnt = 1;
+ of->f_ftype = ftype;
of->f_fid = fid;
+ /* of->f_persistid see smb2_create */
of->f_uniqid = uniqid;
of->f_opened_by_pid = sr->smb_pid;
of->f_granted_access = op->desired_access;
@@ -216,15 +340,15 @@ smb_ofile_open(
of->f_cr = (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) ?
smb_user_getprivcred(sr->uid_user) : sr->uid_user->u_cred;
crhold(of->f_cr);
- of->f_ftype = ftype;
of->f_server = tree->t_server;
of->f_session = tree->t_session;
/*
- * grab a ref for of->f_user
- * released in smb_ofile_delete()
+ * grab a ref for of->f_user and of->f_tree
+ * released in smb_ofile_delete() or smb2_dh_reconnect()
*/
smb_user_hold_internal(sr->uid_user);
+ smb_tree_hold_internal(tree);
of->f_user = sr->uid_user;
of->f_tree = tree;
of->f_node = node;
@@ -291,6 +415,7 @@ smb_ofile_open(
return (of);
errout:
+ smb_tree_release(of->f_tree);
smb_user_release(of->f_user);
crfree(of->f_cr);
@@ -307,24 +432,34 @@ errout:
/*
* smb_ofile_close
+ *
+ * Incoming states: (where from)
+ * SMB_OFILE_STATE_OPEN protocol close, smb_ofile_drop
+ * SMB_OFILE_STATE_EXPIRED called via smb2_dh_expire
+ * SMB_OFILE_STATE_ORPHANED smb2_dh_shutdown
*/
void
smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
{
smb_attr_t *pa;
timestruc_t now;
- uint32_t flags = 0;
SMB_OFILE_VALID(of);
mutex_enter(&of->f_mutex);
ASSERT(of->f_refcnt);
- if (of->f_state != SMB_OFILE_STATE_OPEN) {
+
+ switch (of->f_state) {
+ case SMB_OFILE_STATE_OPEN:
+ case SMB_OFILE_STATE_ORPHANED:
+ case SMB_OFILE_STATE_EXPIRED:
+ of->f_state = SMB_OFILE_STATE_CLOSING;
+ mutex_exit(&of->f_mutex);
+ break;
+ default:
mutex_exit(&of->f_mutex);
return;
}
- of->f_state = SMB_OFILE_STATE_CLOSING;
- mutex_exit(&of->f_mutex);
switch (of->f_ftype) {
case SMB_FTYPE_BYTE_PIPE:
@@ -334,7 +469,10 @@ smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
break;
case SMB_FTYPE_DISK:
- case SMB_FTYPE_PRINTER:
+ if (of->f_persistid != 0)
+ smb_ofile_del_persistid(of);
+ /* FALLTHROUGH */
+ case SMB_FTYPE_PRINTER: /* or FTYPE_DISK */
/*
* In here we make changes to of->f_pending_attr
* while not holding of->f_mutex. This is OK
@@ -362,10 +500,8 @@ smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
}
if (of->f_flags & SMB_OFLAGS_SET_DELETE_ON_CLOSE) {
- if (smb_tree_has_feature(of->f_tree,
- SMB_TREE_CATIA)) {
- flags |= SMB_CATIA;
- }
+ /* We delete using the on-disk name. */
+ uint32_t flags = SMB_CASE_SENSITIVE;
(void) smb_node_set_delete_on_close(of->f_node,
of->f_cr, flags);
}
@@ -433,66 +569,107 @@ smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
smb_server_dec_files(of->f_server);
break;
}
- atomic_dec_32(&of->f_tree->t_open_files);
- mutex_enter(&of->f_mutex);
- ASSERT(of->f_refcnt);
- ASSERT(of->f_state == SMB_OFILE_STATE_CLOSING);
- of->f_state = SMB_OFILE_STATE_CLOSED;
- mutex_exit(&of->f_mutex);
+ /*
+ * Keep f_state == SMB_OFILE_STATE_CLOSING
+ * until the last ref. is dropped, in
+ * smb_ofile_release()
+ */
}
/*
- * smb_ofile_close_all
+ * "Destructor" function for smb_ofile_close_all, and
+ * smb_ofile_close_all_by_pid, called after the llist lock
+ * for tree list has been exited. Our job is to either
+ * close this ofile, or (if durable) set state _SAVE_DH.
*
+ * The next interesting thing happens when the last ref.
+ * on this ofile calls smb_ofile_release(), where we
+ * eihter delete the ofile, or (if durable) leave it
+ * in the persistid hash table for possible reclaim.
*
+ * This is run via smb_llist_post (after smb_llist_exit)
+ * because smb_ofile_close can block, and we'd rather not
+ * block while holding the ofile list as reader.
*/
-void
-smb_ofile_close_all(
- smb_tree_t *tree)
+static void
+smb_ofile_drop(void *arg)
{
- smb_ofile_t *of;
+ smb_ofile_t *of = arg;
- ASSERT(tree);
- ASSERT(tree->t_magic == SMB_TREE_MAGIC);
+ SMB_OFILE_VALID(of);
- smb_llist_enter(&tree->t_ofile_list, RW_READER);
- of = smb_llist_head(&tree->t_ofile_list);
- while (of) {
- ASSERT(of->f_magic == SMB_OFILE_MAGIC);
- ASSERT(of->f_tree == tree);
- of = smb_ofile_close_and_next(of);
+ mutex_enter(&of->f_mutex);
+ switch (of->f_state) {
+ case SMB_OFILE_STATE_OPEN:
+ /* DH checks under mutex. */
+ if (of->f_ftype == SMB_FTYPE_DISK &&
+ of->dh_vers != SMB2_NOT_DURABLE &&
+ smb_dh_should_save(of)) {
+ /*
+ * Tell smb_ofile_release() to
+ * make this an _ORPHANED DH.
+ */
+ of->f_state = SMB_OFILE_STATE_SAVE_DH;
+ mutex_exit(&of->f_mutex);
+ break;
+ }
+ /* OK close it. */
+ mutex_exit(&of->f_mutex);
+ smb_ofile_close(of, 0);
+ break;
+
+ default:
+ /* Something else closed it already. */
+ mutex_exit(&of->f_mutex);
+ break;
}
- smb_llist_exit(&tree->t_ofile_list);
+
+ /*
+ * Release the ref acquired during the traversal loop.
+ * Note that on the last ref, this ofile will be
+ * removed from the tree list etc.
+ * See: smb_llist_post, smb_ofile_delete
+ */
+ smb_ofile_release(of);
}
/*
- * smb_ofiles_close_by_pid
+ * smb_ofile_close_all
*
*
*/
void
-smb_ofile_close_all_by_pid(
+smb_ofile_close_all(
smb_tree_t *tree,
- uint16_t pid)
+ uint32_t pid)
{
smb_ofile_t *of;
+ smb_llist_t *ll;
ASSERT(tree);
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
- smb_llist_enter(&tree->t_ofile_list, RW_READER);
- of = smb_llist_head(&tree->t_ofile_list);
- while (of) {
+ ll = &tree->t_ofile_list;
+
+ smb_llist_enter(ll, RW_READER);
+ for (of = smb_llist_head(ll);
+ of != NULL;
+ of = smb_llist_next(ll, of)) {
ASSERT(of->f_magic == SMB_OFILE_MAGIC);
ASSERT(of->f_tree == tree);
- if (of->f_opened_by_pid == pid) {
- of = smb_ofile_close_and_next(of);
- } else {
- of = smb_llist_next(&tree->t_ofile_list, of);
+ if (pid != 0 && of->f_opened_by_pid != pid)
+ continue;
+ if (smb_ofile_hold(of)) {
+ smb_llist_post(ll, of, smb_ofile_drop);
}
}
- smb_llist_exit(&tree->t_ofile_list);
+
+ /*
+ * Drop the lock and process the llist dtor queue.
+ * Calls smb_ofile_drop on ofiles that were open.
+ */
+ smb_llist_exit(ll);
}
/*
@@ -539,6 +716,53 @@ smb_ofile_enum(smb_ofile_t *of, smb_svcenum_t *svcenum)
}
/*
+ * Take a reference on an open file, in any of the states:
+ * RECONNECT, SAVE_DH, OPEN, ORPHANED.
+ * Return TRUE if ref taken. Used for oplock breaks.
+ *
+ * Note: When the oplock break code calls this, it holds the
+ * node ofile list lock and node oplock mutex. When we see
+ * an ofile in states RECONNECT or SAVING, we know the ofile
+ * is gaining or losing it's tree, and that happens quickly,
+ * so we just wait for that work to finish. However, the
+ * waiting for state transitions here means we have to be
+ * careful not to re-enter the node list lock or otherwise
+ * block on things that could cause a deadlock. Waiting
+ * just on of->f_mutex here is OK.
+ */
+boolean_t
+smb_ofile_hold_olbrk(smb_ofile_t *of)
+{
+ boolean_t ret = B_FALSE;
+
+ ASSERT(of);
+ ASSERT(of->f_magic == SMB_OFILE_MAGIC);
+
+ mutex_enter(&of->f_mutex);
+
+again:
+ switch (of->f_state) {
+ case SMB_OFILE_STATE_RECONNECT:
+ case SMB_OFILE_STATE_SAVING:
+ cv_wait(&of->f_cv, &of->f_mutex);
+ goto again;
+
+ case SMB_OFILE_STATE_OPEN:
+ case SMB_OFILE_STATE_ORPHANED:
+ case SMB_OFILE_STATE_SAVE_DH:
+ of->f_refcnt++;
+ ret = B_TRUE;
+ break;
+
+ default:
+ break;
+ }
+ mutex_exit(&of->f_mutex);
+
+ return (ret);
+}
+
+/*
* Take a reference on an open file.
*/
boolean_t
@@ -564,23 +788,50 @@ smb_ofile_hold(smb_ofile_t *of)
* zero and the file has been closed, post the object for deletion.
* Object deletion is deferred to avoid modifying a list while an
* iteration may be in progress.
+ *
+ * We're careful to avoid dropping f_session etc. until the last
+ * reference goes away. The oplock break code depends on that
+ * not changing while it holds a ref. on an ofile.
*/
void
smb_ofile_release(smb_ofile_t *of)
{
+ smb_tree_t *tree = of->f_tree;
+ boolean_t delete = B_FALSE;
+
SMB_OFILE_VALID(of);
mutex_enter(&of->f_mutex);
- ASSERT(of->f_refcnt);
+ ASSERT(of->f_refcnt > 0);
of->f_refcnt--;
+
switch (of->f_state) {
case SMB_OFILE_STATE_OPEN:
- case SMB_OFILE_STATE_CLOSING:
+ case SMB_OFILE_STATE_ORPHANED:
+ case SMB_OFILE_STATE_EXPIRED:
break;
- case SMB_OFILE_STATE_CLOSED:
- if (of->f_refcnt == 0)
- smb_tree_post_ofile(of->f_tree, of);
+ case SMB_OFILE_STATE_SAVE_DH:
+ ASSERT(tree != NULL);
+ if (of->f_refcnt == 0) {
+ of->f_state = SMB_OFILE_STATE_SAVING;
+ smb_llist_post(&tree->t_ofile_list, of,
+ smb_ofile_save_dh);
+ }
+ break;
+
+ case SMB_OFILE_STATE_CLOSING:
+ /* Note, tree == NULL on _ORPHANED */
+ if (of->f_refcnt == 0) {
+ of->f_state = SMB_OFILE_STATE_CLOSED;
+ if (tree == NULL) {
+ /* Skip smb_llist_post */
+ delete = B_TRUE;
+ break;
+ }
+ smb_llist_post(&tree->t_ofile_list, of,
+ smb_ofile_delete);
+ }
break;
default:
@@ -588,6 +839,15 @@ smb_ofile_release(smb_ofile_t *of)
break;
}
mutex_exit(&of->f_mutex);
+
+ /*
+ * When we drop the last ref. on an expired DH, it's no longer
+ * in any tree, so skip the smb_llist_post and just call
+ * smb_ofile_delete directly.
+ */
+ if (delete) {
+ smb_ofile_delete(of);
+ }
}
/*
@@ -660,6 +920,7 @@ smb_ofile_lookup_by_fid(
goto out;
}
+ /* inline smb_ofile_hold() */
mutex_enter(&of->f_mutex);
if (of->f_state != SMB_OFILE_STATE_OPEN) {
mutex_exit(&of->f_mutex);
@@ -709,6 +970,92 @@ smb_ofile_lookup_by_uniqid(smb_tree_t *tree, uint32_t uniqid)
return (NULL);
}
+static smb_ofile_t *
+smb_ofile_hold_cb(smb_ofile_t *of)
+{
+ smb_ofile_t *ret = of;
+
+ mutex_enter(&of->f_mutex);
+ if (of->f_state == SMB_OFILE_STATE_ORPHANED)
+ /* inline smb_ofile_hold() */
+ of->f_refcnt++;
+ else
+ ret = NULL;
+
+ mutex_exit(&of->f_mutex);
+ return (ret);
+}
+
+/*
+ * Lookup an ofile by persistent ID, and return ONLY if in state ORPHANED
+ * This is used by SMB2 create "reclaim".
+ */
+smb_ofile_t *
+smb_ofile_lookup_by_persistid(smb_request_t *sr, uint64_t persistid)
+{
+ smb_hash_t *hash;
+ smb_bucket_t *bucket;
+ smb_llist_t *ll;
+ smb_ofile_t *of;
+ uint_t idx;
+
+ hash = sr->sr_server->sv_persistid_ht;
+ idx = smb_hash_uint64(hash, persistid);
+ bucket = &hash->buckets[idx];
+ ll = &bucket->b_list;
+
+ smb_llist_enter(ll, RW_READER);
+ of = smb_llist_head(ll);
+ while (of != NULL) {
+ if (of->f_persistid == persistid)
+ break;
+ of = smb_llist_next(ll, of);
+ }
+ if (of != NULL)
+ of = smb_ofile_hold_cb(of);
+ smb_llist_exit(ll);
+
+ return (of);
+}
+
+/*
+ * Create a (unique) persistent ID for a new ofile,
+ * and add this ofile to the persistid hash table.
+ */
+void
+smb_ofile_set_persistid(smb_ofile_t *of)
+{
+ smb_hash_t *hash = of->f_server->sv_persistid_ht;
+ smb_bucket_t *bucket;
+ smb_llist_t *ll;
+ uint_t idx;
+
+ of->f_persistid = SMB_OFILE_PERSISTID(of);
+
+ idx = smb_hash_uint64(hash, of->f_persistid);
+ bucket = &hash->buckets[idx];
+ ll = &bucket->b_list;
+ smb_llist_enter(ll, RW_WRITER);
+ smb_llist_insert_tail(ll, of);
+ smb_llist_exit(ll);
+}
+
+void
+smb_ofile_del_persistid(smb_ofile_t *of)
+{
+ smb_hash_t *hash = of->f_server->sv_persistid_ht;
+ smb_bucket_t *bucket;
+ smb_llist_t *ll;
+ uint_t idx;
+
+ idx = smb_hash_uint64(hash, of->f_persistid);
+ bucket = &hash->buckets[idx];
+ ll = &bucket->b_list;
+ smb_llist_enter(ll, RW_WRITER);
+ smb_llist_remove(ll, of);
+ smb_llist_exit(ll);
+}
+
/*
* Disallow NetFileClose on certain ofiles to avoid side-effects.
* Closing a tree root is not allowed: use NetSessionDel or NetShareDel.
@@ -882,12 +1229,19 @@ smb_ofile_is_open(smb_ofile_t *of)
static boolean_t
smb_ofile_is_open_locked(smb_ofile_t *of)
{
+ ASSERT(MUTEX_HELD(&of->f_mutex));
+
switch (of->f_state) {
case SMB_OFILE_STATE_OPEN:
+ case SMB_OFILE_STATE_SAVE_DH:
+ case SMB_OFILE_STATE_SAVING:
+ case SMB_OFILE_STATE_ORPHANED:
+ case SMB_OFILE_STATE_RECONNECT:
return (B_TRUE);
case SMB_OFILE_STATE_CLOSING:
case SMB_OFILE_STATE_CLOSED:
+ case SMB_OFILE_STATE_EXPIRED:
return (B_FALSE);
default:
@@ -897,75 +1251,105 @@ smb_ofile_is_open_locked(smb_ofile_t *of)
}
/*
- * This function closes the file passed in (if appropriate) and returns the
- * next open file in the list of open files of the tree of the open file passed
- * in. It requires that the list of open files of the tree be entered in
- * RW_READER mode before being called.
+ * smb_ofile_save_dh
+ *
+ * Called via smb_llist_post (after smb_llist_exit) when the last ref.
+ * on this ofile has gone, and this ofile is a "durable handle" (DH)
+ * that has state we've decided to save.
+ *
+ * This does parts of what smb_ofile_delete would do, including:
+ * remove the ofile from the tree ofile list and related.
+ *
+ * We leave the ofile in state ORPHANED, ready for reconnect
+ * or expiration via smb2_dh_expire (see smb_ofile_delete).
*/
-static smb_ofile_t *
-smb_ofile_close_and_next(smb_ofile_t *of)
+static void
+smb_ofile_save_dh(void *arg)
{
- smb_ofile_t *next_of;
- smb_tree_t *tree;
+ smb_ofile_t *of = (smb_ofile_t *)arg;
+ smb_tree_t *tree = of->f_tree;
- ASSERT(of);
- ASSERT(of->f_magic == SMB_OFILE_MAGIC);
+ SMB_OFILE_VALID(of);
+ ASSERT(of->f_refcnt == 0);
+ ASSERT(of->f_ftype == SMB_FTYPE_DISK);
+ ASSERT(of->f_state == SMB_OFILE_STATE_SAVING);
+ atomic_dec_32(&of->f_session->s_file_cnt);
+ atomic_dec_32(&of->f_tree->t_open_files);
+ smb_llist_enter(&tree->t_ofile_list, RW_WRITER);
+ smb_llist_remove(&tree->t_ofile_list, of);
+ smb_llist_exit(&tree->t_ofile_list);
+
+ /*
+ * This ofile is no longer on t_ofile_list, however...
+ *
+ * This is called via smb_llist_post, which means it may run
+ * BEFORE smb_ofile_release drops f_mutex (if another thread
+ * flushes the delete queue before we do). Synchronize.
+ */
mutex_enter(&of->f_mutex);
- switch (of->f_state) {
- case SMB_OFILE_STATE_OPEN:
- /* The file is still open. */
- of->f_refcnt++;
- ASSERT(of->f_refcnt);
- tree = of->f_tree;
- mutex_exit(&of->f_mutex);
- smb_llist_exit(&of->f_tree->t_ofile_list);
- smb_ofile_close(of, 0);
- smb_ofile_release(of);
- smb_llist_enter(&tree->t_ofile_list, RW_READER);
- next_of = smb_llist_head(&tree->t_ofile_list);
- break;
- case SMB_OFILE_STATE_CLOSING:
- case SMB_OFILE_STATE_CLOSED:
- /*
- * The ofile exists but is closed or
- * in the process being closed.
- */
- mutex_exit(&of->f_mutex);
- next_of = smb_llist_next(&of->f_tree->t_ofile_list, of);
- break;
- default:
- ASSERT(0);
- mutex_exit(&of->f_mutex);
- next_of = smb_llist_next(&of->f_tree->t_ofile_list, of);
- break;
- }
- return (next_of);
+ DTRACE_PROBE1(ofile__exit, smb_ofile_t, of);
+ mutex_exit(&of->f_mutex);
+
+ /*
+ * Keep f_notify state, lease, and
+ * keep on node ofile list.
+ * Keep of->f_cr until reclaim.
+ */
+
+ ASSERT(of->f_fid != 0);
+ smb_idpool_free(&tree->t_fid_pool, of->f_fid);
+ of->f_fid = 0;
+ smb_tree_release(of->f_tree);
+ of->f_tree = NULL;
+ smb_user_release(of->f_user);
+ of->f_user = NULL;
+ of->f_session = NULL;
+
+ /*
+ * Make it "orphaned" so it can now be reclaimed.
+ * Note that smb_ofile_hold_olbrk() may have blocked
+ * for state SMB_OFILE_STATE_SAVING, so wake it.
+ */
+ mutex_enter(&of->f_mutex);
+ of->dh_expire_time = gethrtime() + of->dh_timeout_offset;
+ of->f_state = SMB_OFILE_STATE_ORPHANED;
+ cv_broadcast(&of->f_cv);
+ mutex_exit(&of->f_mutex);
}
/*
* Delete an ofile.
*
- * Remove the ofile from the tree list before freeing resources
- * associated with the ofile.
+ * Called via smb_llist_post (after smb_llist_exit)
+ * when the last ref. on this ofile has gone.
+ *
+ * Normally,this removes the ofile from the tree list and
+ * then frees resources held on the ofile. However, when
+ * we're expiring an orphaned durable handle, the linkage
+ * into the tree lists etc. have already been destroyed.
+ * This case is distinguished by of->f_tree == NULL.
*/
-void
+static void
smb_ofile_delete(void *arg)
{
- smb_tree_t *tree;
smb_ofile_t *of = (smb_ofile_t *)arg;
+ smb_tree_t *tree = of->f_tree;
SMB_OFILE_VALID(of);
ASSERT(of->f_refcnt == 0);
ASSERT(of->f_state == SMB_OFILE_STATE_CLOSED);
ASSERT(!SMB_OFILE_OPLOCK_GRANTED(of));
- tree = of->f_tree;
- smb_llist_enter(&tree->t_ofile_list, RW_WRITER);
- smb_llist_remove(&tree->t_ofile_list, of);
- smb_idpool_free(&tree->t_fid_pool, of->f_fid);
- atomic_dec_32(&tree->t_session->s_file_cnt);
- smb_llist_exit(&tree->t_ofile_list);
+ if (tree != NULL) {
+ ASSERT(of->f_user != NULL);
+ ASSERT(of->f_session != NULL);
+ atomic_dec_32(&of->f_session->s_file_cnt);
+ atomic_dec_32(&of->f_tree->t_open_files);
+ smb_llist_enter(&tree->t_ofile_list, RW_WRITER);
+ smb_llist_remove(&tree->t_ofile_list, of);
+ smb_llist_exit(&tree->t_ofile_list);
+ }
/*
* Remove this ofile from the node's n_ofile_list so it
@@ -982,6 +1366,13 @@ smb_ofile_delete(void *arg)
smb_node_rem_ofile(of->f_node, of);
}
+ /*
+ * This ofile is no longer on any lists, however...
+ *
+ * This is called via smb_llist_post, which means it may run
+ * BEFORE smb_ofile_release drops f_mutex (if another thread
+ * flushes the delete queue before we do). Synchronize.
+ */
mutex_enter(&of->f_mutex);
mutex_exit(&of->f_mutex);
@@ -1009,11 +1400,19 @@ smb_ofile_delete(void *arg)
break;
}
+ if (tree != NULL) {
+ if (of->f_fid != 0)
+ smb_idpool_free(&tree->t_fid_pool, of->f_fid);
+ smb_tree_release(of->f_tree);
+ smb_user_release(of->f_user);
+ }
+
+ if (of->f_cr != NULL)
+ crfree(of->f_cr);
+
of->f_magic = (uint32_t)~SMB_OFILE_MAGIC;
list_destroy(&of->f_notify.nc_waiters);
mutex_destroy(&of->f_mutex);
- smb_user_release(of->f_user);
- crfree(of->f_cr);
kmem_cache_free(smb_cache_ofile, of);
}
@@ -1069,19 +1468,21 @@ uint32_t
smb_ofile_open_check(smb_ofile_t *of, uint32_t desired_access,
uint32_t share_access)
{
+ uint32_t ret;
+
ASSERT(of->f_magic == SMB_OFILE_MAGIC);
mutex_enter(&of->f_mutex);
- if (of->f_state != SMB_OFILE_STATE_OPEN) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_INVALID_HANDLE);
+ if (!smb_ofile_is_open_locked(of)) {
+ ret = NT_STATUS_INVALID_HANDLE;
+ goto out;
}
/* if it's just meta data */
if ((of->f_granted_access & FILE_DATA_ALL) == 0) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_SUCCESS);
+ ret = NT_STATUS_SUCCESS;
+ goto out;
}
/*
@@ -1089,42 +1490,44 @@ smb_ofile_open_check(smb_ofile_t *of, uint32_t desired_access,
* open granted (desired) access
*/
if (SMB_DENY_DELETE(share_access) && (of->f_granted_access & DELETE)) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_SHARING_VIOLATION);
+ ret = NT_STATUS_SHARING_VIOLATION;
+ goto out;
}
if (SMB_DENY_READ(share_access) &&
(of->f_granted_access & (FILE_READ_DATA | FILE_EXECUTE))) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_SHARING_VIOLATION);
+ ret = NT_STATUS_SHARING_VIOLATION;
+ goto out;
}
if (SMB_DENY_WRITE(share_access) &&
(of->f_granted_access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_SHARING_VIOLATION);
+ ret = NT_STATUS_SHARING_VIOLATION;
+ goto out;
}
/* check requested desired access against the open share access */
if (SMB_DENY_DELETE(of->f_share_access) && (desired_access & DELETE)) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_SHARING_VIOLATION);
+ ret = NT_STATUS_SHARING_VIOLATION;
+ goto out;
}
if (SMB_DENY_READ(of->f_share_access) &&
(desired_access & (FILE_READ_DATA | FILE_EXECUTE))) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_SHARING_VIOLATION);
+ ret = NT_STATUS_SHARING_VIOLATION;
+ goto out;
}
if (SMB_DENY_WRITE(of->f_share_access) &&
(desired_access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_SHARING_VIOLATION);
+ ret = NT_STATUS_SHARING_VIOLATION;
+ goto out;
}
+ ret = NT_STATUS_SUCCESS;
+out:
mutex_exit(&of->f_mutex);
- return (NT_STATUS_SUCCESS);
+ return (ret);
}
/*
@@ -1139,27 +1542,31 @@ smb_ofile_open_check(smb_ofile_t *of, uint32_t desired_access,
uint32_t
smb_ofile_rename_check(smb_ofile_t *of)
{
+ uint32_t ret;
+
ASSERT(of->f_magic == SMB_OFILE_MAGIC);
mutex_enter(&of->f_mutex);
- if (of->f_state != SMB_OFILE_STATE_OPEN) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_INVALID_HANDLE);
+ if (!smb_ofile_is_open_locked(of)) {
+ ret = NT_STATUS_INVALID_HANDLE;
+ goto out;
}
if ((of->f_granted_access & FILE_DATA_ALL) == 0) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_SUCCESS);
+ ret = NT_STATUS_SUCCESS;
+ goto out;
}
if ((of->f_share_access & FILE_SHARE_DELETE) == 0) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_SHARING_VIOLATION);
+ ret = NT_STATUS_SHARING_VIOLATION;
+ goto out;
}
+ ret = NT_STATUS_SUCCESS;
+out:
mutex_exit(&of->f_mutex);
- return (NT_STATUS_SUCCESS);
+ return (ret);
}
/*
@@ -1183,24 +1590,28 @@ smb_ofile_rename_check(smb_ofile_t *of)
uint32_t
smb_ofile_delete_check(smb_ofile_t *of)
{
+ uint32_t ret;
+
ASSERT(of->f_magic == SMB_OFILE_MAGIC);
mutex_enter(&of->f_mutex);
- if (of->f_state != SMB_OFILE_STATE_OPEN) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_INVALID_HANDLE);
+ if (!smb_ofile_is_open_locked(of)) {
+ ret = NT_STATUS_INVALID_HANDLE;
+ goto out;
}
if (of->f_granted_access &
(FILE_READ_DATA | FILE_WRITE_DATA |
FILE_APPEND_DATA | FILE_EXECUTE | DELETE)) {
- mutex_exit(&of->f_mutex);
- return (NT_STATUS_SHARING_VIOLATION);
+ ret = NT_STATUS_SHARING_VIOLATION;
+ goto out;
}
+ ret = NT_STATUS_SUCCESS;
+out:
mutex_exit(&of->f_mutex);
- return (NT_STATUS_SUCCESS);
+ return (ret);
}
cred_t *
diff --git a/usr/src/uts/common/fs/smbsrv/smb_opipe.c b/usr/src/uts/common/fs/smbsrv/smb_opipe.c
index 7e2d862c77..ac73e9494b 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_opipe.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_opipe.c
@@ -111,9 +111,12 @@ smb_opipe_cancel(smb_request_t *sr)
{
ksocket_t so;
- if (sr->session->s_state == SMB_SESSION_STATE_DISCONNECTED &&
- (so = sr->cancel_arg2) != NULL) {
- (void) ksocket_shutdown(so, SHUT_RDWR, sr->user_cr);
+ switch (sr->session->s_state) {
+ case SMB_SESSION_STATE_DISCONNECTED:
+ case SMB_SESSION_STATE_TERMINATED:
+ if ((so = sr->cancel_arg2) != NULL)
+ (void) ksocket_shutdown(so, SHUT_RDWR, sr->user_cr);
+ break;
}
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_oplock.c b/usr/src/uts/common/fs/smbsrv/smb_oplock.c
index 488a091077..9d78cefead 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_oplock.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_oplock.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -61,7 +61,7 @@ static smb_oplock_grant_t *smb_oplock_exclusive_grant(list_t *);
static smb_oplock_grant_t *smb_oplock_get_grant(smb_oplock_t *, smb_ofile_t *);
static void smb_oplock_sched_async_break(smb_oplock_grant_t *, uint8_t);
-static void smb_oplock_exec_async_break(void *);
+static void smb_oplock_async_break(void *);
static void smb_oplock_break_levelII_locked(smb_node_t *);
/*
@@ -148,8 +148,6 @@ smb_oplock_acquire(smb_request_t *sr, smb_node_t *node, smb_ofile_t *ofile)
ASSERT(RW_LOCK_HELD(&node->n_lock));
op = &sr->sr_open;
- tree = SMB_OFILE_GET_TREE(ofile);
- session = SMB_OFILE_GET_SESSION(ofile);
if (smb_oplocks_enabled == 0 ||
(op->op_oplock_level == SMB_OPLOCK_NONE) ||
@@ -164,6 +162,8 @@ smb_oplock_acquire(smb_request_t *sr, smb_node_t *node, smb_ofile_t *ofile)
mutex_enter(&ol->ol_mutex);
smb_oplock_wait(node);
+ tree = SMB_OFILE_GET_TREE(ofile);
+ session = SMB_OFILE_GET_SESSION(ofile);
/*
* Even if there are no other opens, we might want to
@@ -200,6 +200,20 @@ smb_oplock_acquire(smb_request_t *sr, smb_node_t *node, smb_ofile_t *ofile)
}
og = smb_oplock_set_grant(ofile, op->op_oplock_level);
+
+ /*
+ * When we're sending an oplock break, we may not have a
+ * session (ofile->f_session == NULL) so we use og_dialect
+ * to tell oplock break what kind of break to send.
+ */
+ if (ofile->f_session->dialect >= SMB_VERS_2_BASE)
+ og->og_dialect = DIALECT_SMB2002;
+ else if (op->op_oplock_levelII &&
+ smb_session_levelII_oplocks(session))
+ og->og_dialect = NT_LM_0_12;
+ else
+ og->og_dialect = LANMAN2_1;
+
if (smb_oplock_insert_grant(node, og) != 0) {
smb_oplock_clear_grant(og);
op->op_oplock_level = SMB_OPLOCK_NONE;
@@ -285,7 +299,7 @@ smb_oplock_break(smb_request_t *sr, smb_node_t *node, uint32_t flags)
}
if ((flags & SMB_OPLOCK_BREAK_TO_LEVEL_II) &&
- smb_session_levelII_oplocks(ofile->f_session)) {
+ (og->og_dialect >= NT_LM_0_12)) {
brk = SMB_OPLOCK_BREAK_TO_LEVEL_II;
} else {
brk = SMB_OPLOCK_BREAK_TO_NONE;
@@ -310,7 +324,7 @@ smb_oplock_break(smb_request_t *sr, smb_node_t *node, uint32_t flags)
return (EAGAIN);
}
- if (sr && (sr->uid_user == ofile->f_user)) {
+ if (sr != NULL && sr->uid_user == ofile->f_user) {
timeout = smb_oplock_min_timeout;
} else {
timeout = smb_oplock_timeout;
@@ -390,96 +404,181 @@ smb_oplock_break_levelII_locked(smb_node_t *node)
break;
smb_oplock_sched_async_break(og, SMB_OPLOCK_BREAK_TO_NONE);
- smb_oplock_remove_grant(node, og);
- smb_oplock_clear_grant(og);
+ /*
+ * If oplock break had to do a "local" ack, the
+ * oplock grant will have been already removed.
+ */
+ if (og->og_magic == SMB_OPLOCK_GRANT_MAGIC) {
+ smb_oplock_remove_grant(node, og);
+ smb_oplock_clear_grant(og);
+ }
}
}
/*
- * Schedule a call to smb_session_oplock_break
- * using an smb_request on the owning session.
+ * Schedule an asynchronous call to smb_oplock_async_break.
+ * Try hard to avoid waiting for any oplock work here, or
+ * callers coming in from FEM etc. may suffer delays.
+ *
+ * The caller holds the oplock mutex, and will
+ * signal ol_cv after we return.
+ *
+ * Pulled forward some changes from the leasing work.
*/
static void
smb_oplock_sched_async_break(smb_oplock_grant_t *og, uint8_t brk)
{
- smb_request_t *sr;
- smb_ofile_t *ofile;
+ smb_ofile_t *ofile;
+ smb_request_t *sr = NULL;
+ smb_server_t *sv;
- /*
- * Make sure we can get a hold on the ofile. If we can't,
- * the file is closing, and there's no point scheduling an
- * oplock break on it because the close will release the
- * oplock very soon. Same for the tree & user holds.
- *
- * These holds account for the pointers we copy into the
- * smb_request fields: fid_ofile, tid_tree, uid_user.
- * These holds are released via smb_request_free after
- * the oplock break has been sent.
- */
ofile = og->og_ofile; /* containing struct */
- if (!smb_ofile_hold(ofile))
- return;
+ SMB_OFILE_VALID(ofile);
+ sv = ofile->f_server;
- if ((sr = smb_request_alloc(ofile->f_session, 0)) == NULL) {
- smb_ofile_release(ofile);
+ /*
+ * We're going to schedule a request that will have a
+ * reference to this ofile. Get the hold first.
+ */
+ if (!smb_ofile_hold_olbrk(ofile)) {
+ /* It's closing (or whatever). Nothing to do. */
return;
}
- smb_tree_hold_internal(ofile->f_tree);
- smb_user_hold_internal(ofile->f_user);
+ /*
+ * We need a request allocated on the session that owns
+ * this ofile in order to safely send on that session.
+ *
+ * Note that while we hold a ref. on the ofile, it's
+ * f_session will not change. An ofile in state
+ * _ORPHANED will have f_session == NULL, but the
+ * f_session won't _change_ while we have a ref,
+ * and won't be torn down under our feet.
+ *
+ * If f_session is NULL, or it's in a state that doesn't
+ * allow new requests, use the special "server" session.
+ */
+ if (ofile->f_session != NULL)
+ sr = smb_request_alloc(ofile->f_session, 0);
+ if (sr == NULL)
+ sr = smb_request_alloc(sv->sv_session, 0);
sr->sr_state = SMB_REQ_STATE_SUBMITTED;
sr->user_cr = zone_kcred();
sr->fid_ofile = ofile;
- sr->tid_tree = ofile->f_tree;
- sr->uid_user = ofile->f_user;
-
+ /* Leave tid_tree, uid_user NULL. */
sr->arg.olbrk = *og; /* struct copy */
sr->arg.olbrk.og_breaking = brk;
(void) taskq_dispatch(
- sr->sr_server->sv_worker_pool,
- smb_oplock_exec_async_break, sr, TQ_SLEEP);
+ sv->sv_worker_pool,
+ smb_oplock_async_break, sr, TQ_SLEEP);
}
/*
- * smb_oplock_exec_async_break
+ * smb_oplock_async_break
*
* Called via the taskq to handle an asynchronous oplock break.
- * We have a hold on the ofile, which keeps the FID here valid.
+ * Send an oplock break over the wire, or if we can't,
+ * then process the oplock break locally.
+ *
+ * Note that we have sr->fid_ofile here but all the other
+ * normal sr members are NULL: uid_user, tid_tree.
+ * Also sr->session may or may not be the same session as
+ * the ofile came from (ofile->f_session) depending on
+ * whether this is a "live" open or an orphaned DH,
+ * where ofile->f_session will be NULL.
+ *
+ * Given that we don't always have a session, we determine
+ * the oplock type from f_oplock.og_dialect.
*/
static void
-smb_oplock_exec_async_break(void *arg)
+smb_oplock_async_break(void *arg)
{
- smb_request_t *sr = arg;
+ smb_request_t *sr = arg;
+ smb_ofile_t *ofile = sr->fid_ofile;
smb_oplock_grant_t *og = &sr->arg.olbrk;
+ uint8_t brk = og->og_breaking;
+ int rc;
SMB_REQ_VALID(sr);
- SMB_OPLOCK_GRANT_VALID(og);
mutex_enter(&sr->sr_mutex);
sr->sr_worker = curthread;
- sr->sr_time_active = gethrtime();
+ sr->sr_state = SMB_REQ_STATE_ACTIVE;
+ mutex_exit(&sr->sr_mutex);
+
+ /* smb_oplock_send_brk */
+
+ /*
+ * Build the break message in sr->reply.
+ * It's free'd in smb_request_free().
+ */
+ sr->reply.max_bytes = MLEN;
+ if (og->og_dialect >= DIALECT_SMB2002) {
+ smb2_oplock_break_notification(sr, brk);
+ } else {
+ ASSERT(og->og_dialect > LANMAN2_1 ||
+ brk == SMB_OPLOCK_BREAK_TO_NONE);
+ smb1_oplock_break_notification(sr, brk);
+ }
- switch (sr->sr_state) {
- case SMB_REQ_STATE_SUBMITTED:
- sr->sr_state = SMB_REQ_STATE_ACTIVE;
- mutex_exit(&sr->sr_mutex);
+ /*
+ * Try to send the break message to the client.
+ */
+ if (sr->session == ofile->f_session)
+ rc = smb_session_send(sr->session, 0, &sr->reply);
+ else
+ rc = ENOTCONN;
+ if (rc == 0) {
/*
- * This is where we actually do the deferred work
- * requested by smb_oplock_sched_async_break().
+ * OK, we were able to send the break message.
+ * Caller deals with wait.
+ */
+ goto out;
+ } else {
+ /*
+ * We were unable to send the oplock break request.
+ * Generally, that means we have no connection to this
+ * client right now, and this ofile will have state
+ * SMB_OFILE_STATE_ORPHANED. We either close the handle
+ * or break the oplock locally, in which case the client
+ * gets the updated oplock state when they reconnect.
+ * Decide whether to keep or close.
*/
- smb_session_oplock_break(sr, og->og_breaking);
- mutex_enter(&sr->sr_mutex);
- /* FALLTHROUGH */
+ /*
+ * See similar logic in smb_dh_should_save
+ */
+ switch (ofile->dh_vers) {
+ case SMB2_RESILIENT:
+ break; /* keep DH */
+
+ case SMB2_DURABLE_V2:
+ if (ofile->dh_persist)
+ break; /* keep DH */
+ /* FALLTHROUGH */
+ case SMB2_DURABLE_V1:
+ /* IS durable (v1 or v2) */
+ /* If new_level & BATCH, keep. (not until leases) */
+ /* FALLTHROUGH */
+ case SMB2_NOT_DURABLE:
+ default:
+ smb_ofile_close(ofile, 0);
+ return;
+ }
+ /* Keep this ofile (durable handle). */
- default: /* typically cancelled */
- sr->sr_state = SMB_REQ_STATE_COMPLETED;
- mutex_exit(&sr->sr_mutex);
+ /*
+ * We do the update locally that the client would have
+ * done if they had received our oplock break message.
+ */
+ smb_oplock_ack(ofile->f_node, ofile, brk);
}
+out:
+ sr->sr_state = SMB_REQ_STATE_COMPLETED;
smb_request_free(sr);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_server.c b/usr/src/uts/common/fs/smbsrv/smb_server.c
index 3fbbdded85..f7697039a8 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_server.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_server.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2017 by Delphix. All rights reserved.
*/
@@ -219,11 +219,6 @@
#include <smbsrv/smb_door.h>
#include <smbsrv/smb_kstat.h>
-typedef struct {
- smb_listener_daemon_t *ra_listener;
- smb_session_t *ra_session;
-} smb_receiver_arg_t;
-
static void smb_server_kstat_init(smb_server_t *);
static void smb_server_kstat_fini(smb_server_t *);
static void smb_server_timers(smb_thread_t *, void *);
@@ -250,12 +245,25 @@ static void smb_server_listener_stop(smb_listener_daemon_t *);
static void smb_server_listener(smb_thread_t *, void *);
static void smb_server_receiver(void *);
static void smb_server_create_session(smb_listener_daemon_t *, ksocket_t);
-static void smb_server_destroy_session(smb_listener_daemon_t *,
- smb_session_t *);
+static void smb_server_destroy_session(smb_session_t *);
static uint16_t smb_spool_get_fid(smb_server_t *);
static boolean_t smb_spool_lookup_doc_byfid(smb_server_t *, uint16_t,
smb_kspooldoc_t *);
+/*
+ * How many "buckets" should our hash tables use? On a "real" server,
+ * make them much larger than the number of CPUs we're likely to have.
+ * On "fksmbd" make it smaller so dtrace logs are shorter.
+ * These must be powers of two.
+ */
+#ifdef _KERNEL
+#define DEFAULT_HASH_NBUCKETS 256 /* real server */
+#else
+#define DEFAULT_HASH_NBUCKETS 16 /* for "fksmbd" */
+#endif
+uint32_t SMB_OFILE_HASH_NBUCKETS = DEFAULT_HASH_NBUCKETS;
+uint32_t SMB_LEASE_HASH_NBUCKETS = DEFAULT_HASH_NBUCKETS;
+
int smb_event_debug = 0;
static smb_llist_t smb_servers;
@@ -410,6 +418,12 @@ smb_server_create(void)
cv_init(&sv->sv_cv, NULL, CV_DEFAULT, NULL);
cv_init(&sv->sp_info.sp_cv, NULL, CV_DEFAULT, NULL);
+ sv->sv_persistid_ht = smb_hash_create(sizeof (smb_ofile_t),
+ offsetof(smb_ofile_t, f_dh_lnd), SMB_OFILE_HASH_NBUCKETS);
+
+ smb_llist_constructor(&sv->sv_session_list, sizeof (smb_session_t),
+ offsetof(smb_session_t, s_lnd));
+
smb_llist_constructor(&sv->sv_event_list, sizeof (smb_event_t),
offsetof(smb_event_t, se_lnd));
@@ -510,6 +524,7 @@ smb_server_delete(void)
smb_kshare_fini(sv);
smb_kdoor_fini(sv);
smb_llist_destructor(&sv->sv_event_list);
+ smb_llist_destructor(&sv->sv_session_list);
kmem_free(sv->sv_disp_stats1,
SMB_COM_NUM * sizeof (smb_disp_stats_t));
@@ -521,6 +536,7 @@ smb_server_delete(void)
smb_thread_destroy(&sv->si_thread_timers);
mutex_destroy(&sv->sv_mutex);
+ smb_hash_destroy(sv->sv_persistid_ht);
cv_destroy(&sv->sv_cv);
sv->sv_magic = 0;
kmem_free(sv, sizeof (smb_server_t));
@@ -866,17 +882,11 @@ smb_server_enum(smb_ioc_svcenum_t *ioc)
switch (svcenum->se_type) {
case SMB_SVCENUM_TYPE_USER:
- smb_server_enum_users(&sv->sv_nbt_daemon.ld_session_list,
- svcenum);
- smb_server_enum_users(&sv->sv_tcp_daemon.ld_session_list,
- svcenum);
+ smb_server_enum_users(&sv->sv_session_list, svcenum);
break;
case SMB_SVCENUM_TYPE_TREE:
case SMB_SVCENUM_TYPE_FILE:
- smb_server_enum_trees(&sv->sv_nbt_daemon.ld_session_list,
- svcenum);
- smb_server_enum_trees(&sv->sv_tcp_daemon.ld_session_list,
- svcenum);
+ smb_server_enum_trees(&sv->sv_session_list, svcenum);
break;
default:
rc = EINVAL;
@@ -894,22 +904,18 @@ smb_server_session_close(smb_ioc_session_t *ioc)
{
smb_llist_t *ll;
smb_server_t *sv;
- int nbt_cnt;
- int tcp_cnt;
+ int cnt;
int rc;
if ((rc = smb_server_lookup(&sv)) != 0)
return (rc);
- ll = &sv->sv_nbt_daemon.ld_session_list;
- nbt_cnt = smb_server_session_disconnect(ll, ioc->client, ioc->username);
-
- ll = &sv->sv_tcp_daemon.ld_session_list;
- tcp_cnt = smb_server_session_disconnect(ll, ioc->client, ioc->username);
+ ll = &sv->sv_session_list;
+ cnt = smb_server_session_disconnect(ll, ioc->client, ioc->username);
smb_server_release(sv);
- if ((nbt_cnt == 0) && (tcp_cnt == 0))
+ if (cnt == 0)
return (ENOENT);
return (0);
}
@@ -928,14 +934,9 @@ smb_server_file_close(smb_ioc_fileid_t *ioc)
if ((rc = smb_server_lookup(&sv)) != 0)
return (rc);
- ll = &sv->sv_nbt_daemon.ld_session_list;
+ ll = &sv->sv_session_list;
rc = smb_server_fclose(ll, uniqid);
- if (rc == ENOENT) {
- ll = &sv->sv_tcp_daemon.ld_session_list;
- rc = smb_server_fclose(ll, uniqid);
- }
-
smb_server_release(sv);
return (rc);
}
@@ -949,8 +950,7 @@ smb_server_get_session_count(smb_server_t *sv)
{
uint32_t counter = 0;
- counter = smb_llist_get_count(&sv->sv_nbt_daemon.ld_session_list);
- counter += smb_llist_get_count(&sv->sv_tcp_daemon.ld_session_list);
+ counter = smb_llist_get_count(&sv->sv_session_list);
return (counter);
}
@@ -983,7 +983,7 @@ smb_server_sharevp(smb_server_t *sv, const char *shr_path, vnode_t **vp)
mutex_exit(&sv->sv_mutex);
if ((sr = smb_request_alloc(sv->sv_session, 0)) == NULL) {
- return (ENOMEM);
+ return (ENOTCONN);
}
sr->user_cr = zone_kcred();
@@ -1066,10 +1066,7 @@ smb_server_unshare(const char *sharename)
}
mutex_exit(&sv->sv_mutex);
- ll = &sv->sv_nbt_daemon.ld_session_list;
- smb_server_disconnect_share(ll, sharename);
-
- ll = &sv->sv_tcp_daemon.ld_session_list;
+ ll = &sv->sv_session_list;
smb_server_disconnect_share(ll, sharename);
smb_server_release(sv);
@@ -1093,12 +1090,13 @@ smb_server_disconnect_share(smb_llist_t *ll, const char *sharename)
smb_rwx_rwenter(&session->s_lock, RW_READER);
switch (session->s_state) {
case SMB_SESSION_STATE_NEGOTIATED:
+ smb_rwx_rwexit(&session->s_lock);
smb_session_disconnect_share(session, sharename);
break;
default:
+ smb_rwx_rwexit(&session->s_lock);
break;
}
- smb_rwx_rwexit(&session->s_lock);
session = smb_llist_next(ll, session);
}
@@ -1243,12 +1241,13 @@ smb_server_timers(smb_thread_t *thread, void *arg)
ASSERT(sv != NULL);
/*
- * This just kills old inactive sessions. No urgency.
- * The session code expects one call per minute.
+ * This kills old inactive sessions and expired durable
+ * handles. The session code expects one call per minute.
*/
while (smb_thread_continue_timedwait(thread, 60 /* Seconds */)) {
- smb_session_timers(&sv->sv_nbt_daemon.ld_session_list);
- smb_session_timers(&sv->sv_tcp_daemon.ld_session_list);
+ if (sv->sv_cfg.skc_keepalive != 0)
+ smb_session_timers(sv);
+ smb2_durable_timers(sv);
}
}
@@ -1409,17 +1408,29 @@ smb_server_legacy_kstat_update(kstat_t *ksp, int rw)
static void
smb_server_shutdown(smb_server_t *sv)
{
+ smb_llist_t *sl = &sv->sv_session_list;
+ smb_session_t *session;
+ clock_t time;
+
SMB_SERVER_VALID(sv);
/*
* Stop the listeners first, so we don't get any more
* new work while we're trying to shut down.
- * Also disconnects all sessions under each.
*/
smb_server_listener_stop(&sv->sv_nbt_daemon);
smb_server_listener_stop(&sv->sv_tcp_daemon);
smb_thread_stop(&sv->si_thread_timers);
+ /* Disconnect all of the sessions */
+ smb_llist_enter(sl, RW_READER);
+ session = smb_llist_head(sl);
+ while (session != NULL) {
+ smb_session_disconnect(session);
+ session = smb_llist_next(sl, session);
+ }
+ smb_llist_exit(sl);
+
/*
* Wake up any threads we might have blocked.
* Must precede kdoor_close etc. because those will
@@ -1430,6 +1441,39 @@ smb_server_shutdown(smb_server_t *sv)
smb_threshold_wake_all(&sv->sv_tcon_ct);
smb_threshold_wake_all(&sv->sv_opipe_ct);
+ /*
+ * Wait for the session list to empty.
+ * (cv_signal in smb_server_destroy_session)
+ *
+ * This should not take long, but if there are any leaked
+ * references to ofiles, trees, or users, there could be a
+ * session hanging around. If that happens, the ll_count
+ * never gets to zero and we'll never get the sv_signal.
+ * Defend against that problem using timed wait, then
+ * complain if we find sessions left over and continue
+ * with shutdown in spite of any leaked sessions.
+ * That's better than a server that won't reboot.
+ */
+ time = SEC_TO_TICK(10) + ddi_get_lbolt();
+ mutex_enter(&sv->sv_mutex);
+ while (sv->sv_session_list.ll_count != 0) {
+ if (cv_timedwait(&sv->sv_cv, &sv->sv_mutex, time) < 0)
+ break;
+ }
+ mutex_exit(&sv->sv_mutex);
+#ifdef DEBUG
+ if (sv->sv_session_list.ll_count != 0) {
+ cmn_err(CE_NOTE, "shutdown leaked sessions");
+ debug_enter("shutdown leaked sessions");
+ }
+#endif
+
+ /*
+ * Clean out any durable handles. After this we should
+ * have no ofiles remaining (and no more oplock breaks).
+ */
+ smb2_dh_shutdown(sv);
+
smb_kdoor_close(sv);
#ifdef _KERNEL
smb_kshare_door_fini(sv->sv_lmshrd);
@@ -1437,14 +1481,18 @@ smb_server_shutdown(smb_server_t *sv)
sv->sv_lmshrd = NULL;
smb_export_stop(sv);
+ smb_kshare_stop(sv);
+ /*
+ * Both kshare and the oplock break sub-systems may have
+ * taskq jobs on the spcial "server" session, until we've
+ * closed all ofiles and stopped the kshare exporter.
+ * Now it's safe to destroy the server session, but first
+ * wait for any requests on it to finish. Note that for
+ * normal sessions, this happens in smb_session_cancel,
+ * but that's not called for the server session.
+ */
if (sv->sv_session != NULL) {
- /*
- * smb_kshare_export may have a request on here.
- * Normal sessions do this in smb_session_cancel()
- * but this is a "fake" session used only for the
- * requests used by the kshare thread(s).
- */
smb_slist_wait_for_empty(&sv->sv_session->s_req_list);
smb_session_delete(sv->sv_session);
@@ -1461,7 +1509,6 @@ smb_server_shutdown(smb_server_t *sv)
sv->sv_worker_pool = NULL;
}
- smb_kshare_stop(sv);
smb_server_fsop_stop(sv);
}
@@ -1497,8 +1544,6 @@ smb_server_listener_init(
sizeof (ld->ld_sin6.sin6_addr.s6_addr));
}
- smb_llist_constructor(&ld->ld_session_list, sizeof (smb_session_t),
- offsetof(smb_session_t, s_lnd));
smb_thread_init(&ld->ld_thread, name, smb_server_listener, ld,
smbsrv_listen_pri);
ld->ld_magic = SMB_LISTENER_MAGIC;
@@ -1522,7 +1567,6 @@ smb_server_listener_destroy(smb_listener_daemon_t *ld)
SMB_LISTENER_VALID(ld);
ASSERT(ld->ld_so == NULL);
smb_thread_destroy(&ld->ld_thread);
- smb_llist_destructor(&ld->ld_session_list);
ld->ld_magic = 0;
}
@@ -1620,7 +1664,6 @@ smb_server_listener(smb_thread_t *thread, void *arg)
{
_NOTE(ARGUNUSED(thread))
smb_listener_daemon_t *ld;
- smb_session_t *session;
ksocket_t s_so;
int on;
int txbuf_size;
@@ -1669,14 +1712,6 @@ smb_server_listener(smb_thread_t *thread, void *arg)
smb_server_create_session(ld, s_so);
}
out:
- /* Disconnect all the sessions this listener created. */
- smb_llist_enter(&ld->ld_session_list, RW_READER);
- session = smb_llist_head(&ld->ld_session_list);
- while (session != NULL) {
- smb_session_disconnect(session);
- session = smb_llist_next(&ld->ld_session_list, session);
- }
- smb_llist_exit(&ld->ld_session_list);
ksocket_rele(ld->ld_so);
}
@@ -1684,18 +1719,20 @@ out:
* smb_server_receiver
*
* Entry point of the receiver threads.
+ * Also does cleanup when socket disconnected.
*/
static void
smb_server_receiver(void *arg)
{
- smb_listener_daemon_t *ld;
- smb_session_t *session;
+ smb_session_t *session;
+
+ session = (smb_session_t *)arg;
- ld = ((smb_receiver_arg_t *)arg)->ra_listener;
- session = ((smb_receiver_arg_t *)arg)->ra_session;
- smb_mem_free(arg);
+ /* We stay in here until socket disconnect. */
smb_session_receiver(session);
- smb_server_destroy_session(ld, session);
+
+ ASSERT(session->s_state == SMB_SESSION_STATE_SHUTDOWN);
+ smb_server_destroy_session(session);
}
/*
@@ -1849,48 +1886,37 @@ smb_server_session_disconnect(smb_llist_t *ll,
smb_session_t *sn;
smb_llist_t *ulist;
smb_user_t *user;
- boolean_t match;
int count = 0;
smb_llist_enter(ll, RW_READER);
- sn = smb_llist_head(ll);
- while (sn != NULL) {
+ for (sn = smb_llist_head(ll);
+ sn != NULL;
+ sn = smb_llist_next(ll, sn)) {
SMB_SESSION_VALID(sn);
- if ((*client != '\0') && (!smb_session_isclient(sn, client))) {
- sn = smb_llist_next(ll, sn);
+ if (*client != '\0' && !smb_session_isclient(sn, client))
continue;
- }
ulist = &sn->s_user_list;
smb_llist_enter(ulist, RW_READER);
- user = smb_llist_head(ulist);
- while (user != NULL) {
- if (smb_user_hold(user)) {
- match = (*name == '\0');
- if (!match)
- match = smb_user_namecmp(user, name);
-
- if (match) {
- smb_llist_exit(ulist);
- smb_user_logoff(user);
- ++count;
- smb_user_release(user);
- smb_llist_enter(ulist, RW_READER);
- user = smb_llist_head(ulist);
- continue;
- }
+ for (user = smb_llist_head(ulist);
+ user != NULL;
+ user = smb_llist_next(ulist, user)) {
+ SMB_USER_VALID(user);
+
+ if (*name != '\0' && !smb_user_namecmp(user, name))
+ continue;
+ if (smb_user_hold(user)) {
+ smb_user_logoff(user);
smb_user_release(user);
+ count++;
}
-
- user = smb_llist_next(ulist, user);
}
smb_llist_exit(ulist);
- sn = smb_llist_next(ll, sn);
}
smb_llist_exit(ll);
@@ -1934,6 +1960,63 @@ smb_server_fclose(smb_llist_t *ll, uint32_t uniqid)
return (rc);
}
+/*
+ * This is used by SMB2 session setup to logoff a previous session,
+ * so it can force a logoff that we haven't noticed yet.
+ * This is not called frequently, so we just walk the list of
+ * connections searching for the user.
+ */
+void
+smb_server_logoff_ssnid(smb_request_t *sr, uint64_t ssnid)
+{
+ smb_server_t *sv = sr->sr_server;
+ smb_llist_t *sess_list;
+ smb_session_t *sess;
+
+ if (sv->sv_state != SMB_SERVER_STATE_RUNNING)
+ return;
+
+ sess_list = &sv->sv_session_list;
+ smb_llist_enter(sess_list, RW_READER);
+
+ for (sess = smb_llist_head(sess_list);
+ sess != NULL;
+ sess = smb_llist_next(sess_list, sess)) {
+
+ smb_user_t *user;
+
+ SMB_SESSION_VALID(sess);
+
+ if (sess->dialect < SMB_VERS_2_BASE)
+ continue;
+
+ if (sess->s_state != SMB_SESSION_STATE_NEGOTIATED)
+ continue;
+
+ user = smb_session_lookup_ssnid(sess, ssnid);
+ if (user == NULL)
+ continue;
+
+ if (!smb_is_same_user(user->u_cred, sr->user_cr)) {
+ smb_user_release(user);
+ continue;
+ }
+
+ /* Treat this as if we lost the connection */
+ user->preserve_opens = SMB2_DH_PRESERVE_SOME;
+ smb_user_logoff(user);
+ smb_user_release(user);
+
+ /*
+ * The above may have left work on the delete queues
+ */
+ smb_llist_flush(&sess->s_tree_list);
+ smb_llist_flush(&sess->s_user_list);
+ }
+
+ smb_llist_exit(sess_list);
+}
+
/* See also: libsmb smb_kmod_setcfg */
static void
smb_server_store_cfg(smb_server_t *sv, smb_ioc_cfg_t *ioc)
@@ -1941,11 +2024,6 @@ smb_server_store_cfg(smb_server_t *sv, smb_ioc_cfg_t *ioc)
if (ioc->maxconnections == 0)
ioc->maxconnections = 0xFFFFFFFF;
- smb_session_correct_keep_alive_values(
- &sv->sv_nbt_daemon.ld_session_list, ioc->keepalive);
- smb_session_correct_keep_alive_values(
- &sv->sv_tcp_daemon.ld_session_list, ioc->keepalive);
-
sv->sv_cfg.skc_maxworkers = ioc->maxworkers;
sv->sv_cfg.skc_maxconnections = ioc->maxconnections;
sv->sv_cfg.skc_keepalive = ioc->keepalive;
@@ -2343,10 +2421,11 @@ static void
smb_server_create_session(smb_listener_daemon_t *ld, ksocket_t s_so)
{
smb_session_t *session;
- smb_receiver_arg_t *rarg;
taskqid_t tqid;
+ smb_llist_t *sl;
+ smb_server_t *sv = ld->ld_sv;
- session = smb_session_create(s_so, ld->ld_port, ld->ld_sv,
+ session = smb_session_create(s_so, ld->ld_port, sv,
ld->ld_family);
if (session == NULL) {
@@ -2356,25 +2435,20 @@ smb_server_create_session(smb_listener_daemon_t *ld, ksocket_t s_so)
return;
}
- smb_llist_enter(&ld->ld_session_list, RW_WRITER);
- smb_llist_insert_tail(&ld->ld_session_list, session);
- smb_llist_exit(&ld->ld_session_list);
-
- rarg = (smb_receiver_arg_t *)smb_mem_alloc(
- sizeof (smb_receiver_arg_t));
- rarg->ra_listener = ld;
- rarg->ra_session = session;
+ sl = &sv->sv_session_list;
+ smb_llist_enter(sl, RW_WRITER);
+ smb_llist_insert_tail(sl, session);
+ smb_llist_exit(sl);
/*
* These taskq entries must run independently of one another,
* so TQ_NOQUEUE. TQ_SLEEP (==0) just for clarity.
*/
- tqid = taskq_dispatch(ld->ld_sv->sv_receiver_pool,
- smb_server_receiver, rarg, TQ_NOQUEUE | TQ_SLEEP);
+ tqid = taskq_dispatch(sv->sv_receiver_pool,
+ smb_server_receiver, session, TQ_NOQUEUE | TQ_SLEEP);
if (tqid == TASKQID_INVALID) {
- smb_mem_free(rarg);
smb_session_disconnect(session);
- smb_server_destroy_session(ld, session);
+ smb_server_destroy_session(session);
cmn_err(CE_WARN, "SMB Session: taskq_dispatch failed");
return;
}
@@ -2383,10 +2457,41 @@ smb_server_create_session(smb_listener_daemon_t *ld, ksocket_t s_so)
}
static void
-smb_server_destroy_session(smb_listener_daemon_t *ld, smb_session_t *session)
+smb_server_destroy_session(smb_session_t *session)
{
- smb_llist_enter(&ld->ld_session_list, RW_WRITER);
- smb_llist_remove(&ld->ld_session_list, session);
- smb_llist_exit(&ld->ld_session_list);
+ smb_server_t *sv;
+ smb_llist_t *ll;
+ uint32_t count;
+
+ ASSERT(session->s_server != NULL);
+ sv = session->s_server;
+ ll = &sv->sv_session_list;
+
+ smb_llist_flush(&session->s_tree_list);
+ smb_llist_flush(&session->s_user_list);
+
+ /*
+ * The user and tree lists should be empty now.
+ */
+#ifdef DEBUG
+ if (session->s_user_list.ll_count != 0) {
+ cmn_err(CE_WARN, "user list not empty?");
+ debug_enter("s_user_list");
+ }
+ if (session->s_tree_list.ll_count != 0) {
+ cmn_err(CE_WARN, "tree list not empty?");
+ debug_enter("s_tree_list");
+ }
+#endif
+
+ smb_llist_enter(ll, RW_WRITER);
+ smb_llist_remove(ll, session);
+ count = ll->ll_count;
+ smb_llist_exit(ll);
+
smb_session_delete(session);
+ if (count == 0) {
+ /* See smb_server_shutdown */
+ cv_signal(&sv->sv_cv);
+ }
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_session.c b/usr/src/uts/common/fs/smbsrv/smb_session.c
index c3ce565688..8989fdb07d 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_session.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_session.c
@@ -74,14 +74,29 @@ static int smb_session_xprt_puthdr(smb_session_t *,
uint8_t *dst, size_t dstlen);
static smb_tree_t *smb_session_get_tree(smb_session_t *, smb_tree_t *);
static void smb_session_logoff(smb_session_t *);
+static void smb_session_disconnect_trees(smb_session_t *);
static void smb_request_init_command_mbuf(smb_request_t *sr);
static void smb_session_genkey(smb_session_t *);
+/*
+ * This (legacy) code is in support of an "idle timeout" feature,
+ * which is apparently incomplete. To complete it, we should:
+ * when the keep_alive timer expires, check whether the client
+ * has any open files, and if not then kill their session.
+ * Right now the timers are there, but nothing happens when
+ * a timer expires.
+ *
+ * Todo: complete logic to kill idle sessions.
+ *
+ * Only called when sv_cfg.skc_keepalive != 0
+ */
void
-smb_session_timers(smb_llist_t *ll)
+smb_session_timers(smb_server_t *sv)
{
smb_session_t *session;
+ smb_llist_t *ll;
+ ll = &sv->sv_session_list;
smb_llist_enter(ll, RW_READER);
session = smb_llist_head(ll);
while (session != NULL) {
@@ -93,40 +108,8 @@ smb_session_timers(smb_llist_t *ll)
if (session->keep_alive &&
(session->keep_alive != (uint32_t)-1))
session->keep_alive--;
- session = smb_llist_next(ll, session);
- }
- smb_llist_exit(ll);
-}
-
-void
-smb_session_correct_keep_alive_values(smb_llist_t *ll, uint32_t new_keep_alive)
-{
- smb_session_t *sn;
- /*
- * Caller specifies seconds, but we track in minutes, so
- * convert to minutes (rounded up).
- */
- new_keep_alive = (new_keep_alive + 59) / 60;
-
- if (new_keep_alive == smb_keep_alive)
- return;
- /*
- * keep alive == 0 means do not drop connection if it's idle
- */
- smb_keep_alive = (new_keep_alive) ? new_keep_alive : -1;
-
- /*
- * Walk through the table and set each session to the new keep_alive
- * value if they have not already timed out. Block clock interrupts.
- */
- smb_llist_enter(ll, RW_READER);
- sn = smb_llist_head(ll);
- while (sn != NULL) {
- SMB_SESSION_VALID(sn);
- if (sn->keep_alive != 0)
- sn->keep_alive = new_keep_alive;
- sn = smb_llist_next(ll, sn);
+ session = smb_llist_next(ll, session);
}
smb_llist_exit(ll);
}
@@ -242,9 +225,9 @@ smb_netbios_session_request(struct smb_session *session)
int rc;
char *calling_name;
char *called_name;
- char client_name[NETBIOS_NAME_SZ];
- struct mbuf_chain mbc;
- char *names = NULL;
+ char client_name[NETBIOS_NAME_SZ];
+ struct mbuf_chain mbc;
+ char *names = NULL;
smb_wchar_t *wbuf = NULL;
smb_xprt_t hdr;
char *p;
@@ -491,6 +474,11 @@ smb_request_cancel(smb_request_t *sr)
* smb_session_receiver
*
* Receives request from the network and dispatches them to a worker.
+ *
+ * When we receive a disconnect here, it _could_ be due to the server
+ * having initiated disconnect, in which case the session state will be
+ * SMB_SESSION_STATE_TERMINATED and we want to keep that state so later
+ * tear-down logic will know which side initiated.
*/
void
smb_session_receiver(smb_session_t *session)
@@ -505,7 +493,9 @@ smb_session_receiver(smb_session_t *session)
rc = smb_netbios_session_request(session);
if (rc != 0) {
smb_rwx_rwenter(&session->s_lock, RW_WRITER);
- session->s_state = SMB_SESSION_STATE_DISCONNECTED;
+ if (session->s_state != SMB_SESSION_STATE_TERMINATED)
+ session->s_state =
+ SMB_SESSION_STATE_DISCONNECTED;
smb_rwx_rwexit(&session->s_lock);
return;
}
@@ -518,7 +508,8 @@ smb_session_receiver(smb_session_t *session)
(void) smb_session_reader(session);
smb_rwx_rwenter(&session->s_lock, RW_WRITER);
- session->s_state = SMB_SESSION_STATE_DISCONNECTED;
+ if (session->s_state != SMB_SESSION_STATE_TERMINATED)
+ session->s_state = SMB_SESSION_STATE_DISCONNECTED;
smb_rwx_rwexit(&session->s_lock);
smb_soshutdown(session->sock);
@@ -536,7 +527,7 @@ smb_session_receiver(smb_session_t *session)
/*
* smb_session_disconnect
*
- * Disconnects the session passed in.
+ * Server-initiated disconnect (i.e. server shutdown)
*/
void
smb_session_disconnect(smb_session_t *session)
@@ -550,8 +541,8 @@ smb_session_disconnect(smb_session_t *session)
case SMB_SESSION_STATE_ESTABLISHED:
case SMB_SESSION_STATE_NEGOTIATED:
smb_soshutdown(session->sock);
- session->s_state = SMB_SESSION_STATE_DISCONNECTED;
- _NOTE(FALLTHRU)
+ session->s_state = SMB_SESSION_STATE_TERMINATED;
+ break;
case SMB_SESSION_STATE_DISCONNECTED:
case SMB_SESSION_STATE_TERMINATED:
break;
@@ -622,8 +613,8 @@ smb_session_reader(smb_session_t *session)
/*
* Allocate a request context, read the whole message.
- * If the request alloc fails, we've disconnected and
- * won't be able to send the reply anyway, so bail now.
+ * If the request alloc fails, we've disconnected
+ * and won't be able to send the reply anyway, so bail now.
*/
if ((sr = smb_request_alloc(session, hdr.xh_length)) == NULL)
break;
@@ -733,6 +724,7 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
now = ddi_get_lbolt64();
+ session->s_server = sv;
session->s_kid = SMB_NEW_KID();
session->s_state = SMB_SESSION_STATE_INITIALIZED;
session->native_os = NATIVE_OS_UNKNOWN;
@@ -802,7 +794,6 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
else
smb_server_inc_tcp_sess(sv);
}
- session->s_server = sv;
smb_server_get_cfg(sv, &session->s_cfg);
session->s_srqueue = &sv->sv_srqueue;
@@ -925,13 +916,23 @@ smb_session_cancel_requests(
smb_user_t *
smb_session_lookup_uid(smb_session_t *session, uint16_t uid)
{
- return (smb_session_lookup_uid_st(session, uid,
+ return (smb_session_lookup_uid_st(session, 0, uid,
+ SMB_USER_STATE_LOGGED_ON));
+}
+
+/*
+ * Find a user on the specified session by SMB2 SSNID.
+ */
+smb_user_t *
+smb_session_lookup_ssnid(smb_session_t *session, uint64_t ssnid)
+{
+ return (smb_session_lookup_uid_st(session, ssnid, 0,
SMB_USER_STATE_LOGGED_ON));
}
smb_user_t *
-smb_session_lookup_uid_st(smb_session_t *session, uint16_t uid,
- smb_user_state_t st)
+smb_session_lookup_uid_st(smb_session_t *session, uint64_t ssnid,
+ uint16_t uid, smb_user_state_t st)
{
smb_user_t *user;
smb_llist_t *user_list;
@@ -941,34 +942,30 @@ smb_session_lookup_uid_st(smb_session_t *session, uint16_t uid,
user_list = &session->s_user_list;
smb_llist_enter(user_list, RW_READER);
- user = smb_llist_head(user_list);
- while (user) {
+ for (user = smb_llist_head(user_list);
+ user != NULL;
+ user = smb_llist_next(user_list, user)) {
+
SMB_USER_VALID(user);
ASSERT(user->u_session == session);
- if (user->u_uid == uid && user->u_state == st) {
- smb_user_hold_internal(user);
+ if (user->u_ssnid != ssnid && user->u_uid != uid)
+ continue;
+
+ mutex_enter(&user->u_mutex);
+ if (user->u_state == st) {
+ // smb_user_hold_internal(user);
+ user->u_refcnt++;
+ mutex_exit(&user->u_mutex);
break;
}
-
- user = smb_llist_next(user_list, user);
+ mutex_exit(&user->u_mutex);
}
smb_llist_exit(user_list);
return (user);
}
-void
-smb_session_post_user(smb_session_t *session, smb_user_t *user)
-{
- SMB_USER_VALID(user);
- ASSERT(user->u_refcnt == 0);
- ASSERT(user->u_state == SMB_USER_STATE_LOGGED_OFF);
- ASSERT(user->u_session == session);
-
- smb_llist_post(&session->s_user_list, user, smb_user_delete);
-}
-
/*
* Find a tree by tree-id.
*/
@@ -976,7 +973,6 @@ smb_tree_t *
smb_session_lookup_tree(
smb_session_t *session,
uint16_t tid)
-
{
smb_tree_t *tree;
@@ -1148,8 +1144,9 @@ smb_session_disconnect_owned_trees(
/*
* smb_tree_hold() succeeded, hence we are in state
* SMB_TREE_STATE_CONNECTED; schedule this tree
- * for asynchronous disconnect, which will fire
- * after we drop the llist traversal lock.
+ * for disconnect after smb_llist_exit because
+ * the "unmap exec" up-call can block, and we'd
+ * rather not block with the tree list locked.
*/
smb_llist_post(tree_list, tree, smb_session_tree_dtor);
}
@@ -1163,11 +1160,11 @@ smb_session_disconnect_owned_trees(
/*
* Disconnect all trees that this user has connected.
*/
-void
+static void
smb_session_disconnect_trees(
smb_session_t *session)
{
- smb_tree_t *tree;
+ smb_tree_t *tree, *next_tree;
SMB_SESSION_VALID(session);
@@ -1176,8 +1173,9 @@ smb_session_disconnect_trees(
ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
ASSERT(tree->t_session == session);
smb_tree_disconnect(tree, B_TRUE);
+ next_tree = smb_session_get_tree(session, tree);
smb_tree_release(tree);
- tree = smb_session_get_tree(session, NULL);
+ tree = next_tree;
}
}
@@ -1206,18 +1204,6 @@ smb_session_disconnect_share(
}
}
-void
-smb_session_post_tree(smb_session_t *session, smb_tree_t *tree)
-{
- SMB_SESSION_VALID(session);
- SMB_TREE_VALID(tree);
- ASSERT0(tree->t_refcnt);
- ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
- ASSERT(tree->t_session == session);
-
- smb_llist_post(&session->s_tree_list, tree, smb_tree_dealloc);
-}
-
/*
* Get the next connected tree in the list. A reference is taken on
* the tree, which can be released later with smb_tree_release().
@@ -1260,44 +1246,110 @@ smb_session_get_tree(
/*
* Logoff all users associated with the specified session.
+ *
+ * This is called for both server-initiated disconnect
+ * (SMB_SESSION_STATE_TERMINATED) and client-initiated
+ * disconnect (SMB_SESSION_STATE_DISCONNECTED).
+ * If client-initiated, save durable handles.
*/
static void
smb_session_logoff(smb_session_t *session)
{
+ smb_llist_t *ulist;
smb_user_t *user;
SMB_SESSION_VALID(session);
- smb_session_disconnect_trees(session);
-
- smb_llist_enter(&session->s_user_list, RW_READER);
+top:
+ ulist = &session->s_user_list;
+ smb_llist_enter(ulist, RW_READER);
- user = smb_llist_head(&session->s_user_list);
+ user = smb_llist_head(ulist);
while (user) {
SMB_USER_VALID(user);
ASSERT(user->u_session == session);
+ mutex_enter(&user->u_mutex);
switch (user->u_state) {
case SMB_USER_STATE_LOGGING_ON:
case SMB_USER_STATE_LOGGED_ON:
- smb_user_hold_internal(user);
+ // smb_user_hold_internal(user);
+ user->u_refcnt++;
+ mutex_exit(&user->u_mutex);
+ if (user->u_session->s_state ==
+ SMB_SESSION_STATE_DISCONNECTED)
+ user->preserve_opens = SMB2_DH_PRESERVE_ALL;
smb_user_logoff(user);
smb_user_release(user);
break;
case SMB_USER_STATE_LOGGED_OFF:
case SMB_USER_STATE_LOGGING_OFF:
+ mutex_exit(&user->u_mutex);
break;
default:
ASSERT(0);
+ mutex_exit(&user->u_mutex);
break;
}
- user = smb_llist_next(&session->s_user_list, user);
+ user = smb_llist_next(ulist, user);
+ }
+
+ /* Needed below (Was the list empty?) */
+ user = smb_llist_head(ulist);
+
+ smb_llist_exit(ulist);
+
+ /*
+ * It's possible for user objects to remain due to references
+ * obtained via smb_server_lookup_ssnid(), when an SMB2
+ * session setup is destroying a previous session.
+ *
+ * Wait for user objects to clear out (last refs. go away,
+ * then smb_user_delete takes them out of the list). When
+ * the last user object is removed, the session state is
+ * set to SHUTDOWN and s_lock is signaled.
+ *
+ * Not all places that call smb_user_release necessarily
+ * flush the delete queue, so after we wait for the list
+ * to empty out, go back to the top and recheck the list
+ * delete queue to make sure smb_user_delete happens.
+ */
+ if (user == NULL) {
+ /* User list is empty. */
+ smb_rwx_rwenter(&session->s_lock, RW_WRITER);
+ session->s_state = SMB_SESSION_STATE_SHUTDOWN;
+ smb_rwx_rwexit(&session->s_lock);
+ } else {
+ smb_rwx_rwenter(&session->s_lock, RW_READER);
+ if (session->s_state != SMB_SESSION_STATE_SHUTDOWN) {
+ (void) smb_rwx_cvwait(&session->s_lock,
+ MSEC_TO_TICK(200));
+ smb_rwx_rwexit(&session->s_lock);
+ goto top;
+ }
+ smb_rwx_rwexit(&session->s_lock);
+ }
+ ASSERT(session->s_state == SMB_SESSION_STATE_SHUTDOWN);
+
+ /*
+ * User list should be empty now.
+ */
+#ifdef DEBUG
+ if (ulist->ll_count != 0) {
+ cmn_err(CE_WARN, "user list not empty?");
+ debug_enter("s_user_list");
}
+#endif
- smb_llist_exit(&session->s_user_list);
+ /*
+ * User logoff happens first so we'll set preserve_opens
+ * for client-initiated disconnect. When that's done
+ * there should be no trees left, but check anyway.
+ */
+ smb_session_disconnect_trees(session);
}
/*
@@ -1400,6 +1452,7 @@ smb_request_alloc(smb_session_t *session, int req_length)
ASSERT(0);
/* FALLTHROUGH */
case SMB_SESSION_STATE_DISCONNECTED:
+ case SMB_SESSION_STATE_SHUTDOWN:
case SMB_SESSION_STATE_TERMINATED:
/* Disallow new requests in these states. */
if (sr->sr_request_buf)
@@ -1491,34 +1544,6 @@ smb_session_levelII_oplocks(smb_session_t *session)
return (B_FALSE);
}
-/*
- * smb_session_oplock_break
- *
- * Send an oplock break request to the client,
- * recalling some cache delegation.
- */
-void
-smb_session_oplock_break(smb_request_t *sr, uint8_t brk)
-{
- smb_session_t *session = sr->session;
- mbuf_chain_t *mbc = &sr->reply;
-
- SMB_SESSION_VALID(session);
-
- /*
- * Build the break message in sr->reply and then send it.
- * The mbc is free'd later, in smb_request_free().
- */
- mbc->max_bytes = MLEN;
- if (session->dialect <= NT_LM_0_12) {
- smb1_oplock_break_notification(sr, brk);
- } else {
- smb2_oplock_break_notification(sr, brk);
- }
-
- (void) smb_session_send(session, 0, mbc);
-}
-
static void
smb_session_genkey(smb_session_t *session)
{
diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree.c b/usr/src/uts/common/fs/smbsrv/smb_tree.c
index 6e86e491d6..af1f54f968 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_tree.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_tree.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2016 by Delphix. All rights reserved.
*/
@@ -122,10 +122,14 @@
*
* Transition T2
*
+ * This transition occurs in smb_tree_disconnect()
+ *
+ * Transition T3
+ *
* This transition occurs in smb_tree_release(). The resources associated
* with the tree are freed as well as the tree structure. For the transition
- * to occur, the tree must be in the SMB_TREE_STATE_DISCONNECTED state and
- * the reference count be zero.
+ * to occur, the tree must be in the SMB_TREE_STATE_DISCONNECTED and the
+ * reference count must be zero.
*
* Comments
* --------
@@ -144,7 +148,11 @@
* Rules of access to a tree structure:
*
* 1) In order to avoid deadlocks, when both (mutex and lock of the user
- * list) have to be entered, the lock must be entered first.
+ * list) have to be entered, the lock must be entered first. Additionally,
+ * when both the (mutex and lock of the ofile list) have to be entered,
+ * the mutex must be entered first. However, the ofile list lock must NOT
+ * be dropped while the mutex is held in such a way that the ofile deleteq
+ * is flushed.
*
* 2) All actions applied to a tree require a reference count.
*
@@ -178,8 +186,8 @@ uint32_t smb_tree_connect_printq(smb_request_t *, smb_arg_tcon_t *);
uint32_t smb_tree_connect_ipc(smb_request_t *, smb_arg_tcon_t *);
static smb_tree_t *smb_tree_alloc(smb_request_t *, const smb_kshare_t *,
smb_node_t *, uint32_t, uint32_t);
+static void smb_tree_dealloc(void *);
static boolean_t smb_tree_is_connected_locked(smb_tree_t *);
-static boolean_t smb_tree_is_disconnected(smb_tree_t *);
static char *smb_tree_get_sharename(char *);
static int smb_tree_getattr(const smb_kshare_t *, smb_node_t *, smb_tree_t *);
static void smb_tree_get_volname(vfs_t *, smb_tree_t *);
@@ -297,7 +305,7 @@ smb_tree_disconnect(smb_tree_t *tree, boolean_t do_exec)
/*
* The files opened under this tree are closed.
*/
- smb_ofile_close_all(tree);
+ smb_ofile_close_all(tree, 0);
/*
* The directories opened under this tree are closed.
*/
@@ -371,42 +379,32 @@ smb_tree_release(
{
SMB_TREE_VALID(tree);
- mutex_enter(&tree->t_mutex);
- ASSERT(tree->t_refcnt);
- tree->t_refcnt--;
-
/* flush the ofile and odir lists' delete queues */
smb_llist_flush(&tree->t_ofile_list);
smb_llist_flush(&tree->t_odir_list);
- if (smb_tree_is_disconnected(tree) && (tree->t_refcnt == 0))
- smb_session_post_tree(tree->t_session, tree);
-
- mutex_exit(&tree->t_mutex);
-}
-
-void
-smb_tree_post_ofile(smb_tree_t *tree, smb_ofile_t *of)
-{
- SMB_TREE_VALID(tree);
- SMB_OFILE_VALID(of);
- ASSERT(of->f_refcnt == 0);
- ASSERT(of->f_state == SMB_OFILE_STATE_CLOSED);
- ASSERT(of->f_tree == tree);
-
- smb_llist_post(&tree->t_ofile_list, of, smb_ofile_delete);
-}
+ mutex_enter(&tree->t_mutex);
+ ASSERT(tree->t_refcnt);
+ tree->t_refcnt--;
-void
-smb_tree_post_odir(smb_tree_t *tree, smb_odir_t *od)
-{
- SMB_TREE_VALID(tree);
- SMB_ODIR_VALID(od);
- ASSERT(od->d_refcnt == 0);
- ASSERT(od->d_state == SMB_ODIR_STATE_CLOSED);
- ASSERT(od->d_tree == tree);
+ switch (tree->t_state) {
+ case SMB_TREE_STATE_DISCONNECTED:
+ if (tree->t_refcnt == 0) {
+ smb_session_t *ssn = tree->t_session;
+ tree->t_state = SMB_TREE_STATE_DISCONNECTED;
+ smb_llist_post(&ssn->s_tree_list, tree,
+ smb_tree_dealloc);
+ }
+ break;
+ case SMB_TREE_STATE_CONNECTED:
+ case SMB_TREE_STATE_DISCONNECTING:
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
- smb_llist_post(&tree->t_odir_list, od, smb_odir_delete);
+ mutex_exit(&tree->t_mutex);
}
/*
@@ -420,7 +418,7 @@ smb_tree_close_pid(
ASSERT(tree);
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
- smb_ofile_close_all_by_pid(tree, pid);
+ smb_ofile_close_all(tree, pid);
smb_tree_close_odirs(tree, pid);
}
@@ -484,6 +482,11 @@ smb_tree_fclose(smb_tree_t *tree, uint32_t uniqid)
ASSERT(tree);
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
+ /*
+ * Note that ORPHANED ofiles aren't fclosable, as they have
+ * no session, user, or tree by which they might be found.
+ * They will eventually expire.
+ */
if ((of = smb_ofile_lookup_by_uniqid(tree, uniqid)) == NULL)
return (ENOENT);
@@ -650,7 +653,7 @@ smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon)
smb_user_t *user = sr->uid_user;
smb_node_t *dnode = NULL;
smb_node_t *snode = NULL;
- smb_kshare_t *si = tcon->si;
+ smb_kshare_t *si = tcon->si;
char *service = tcon->service;
char last_component[MAXNAMELEN];
smb_tree_t *tree;
@@ -772,7 +775,7 @@ smb_tree_connect_printq(smb_request_t *sr, smb_arg_tcon_t *tcon)
smb_user_t *user = sr->uid_user;
smb_node_t *dnode = NULL;
smb_node_t *snode = NULL;
- smb_kshare_t *si = tcon->si;
+ smb_kshare_t *si = tcon->si;
char *service = tcon->service;
char last_component[MAXNAMELEN];
smb_tree_t *tree;
@@ -922,7 +925,7 @@ smb_tree_alloc(smb_request_t *sr, const smb_kshare_t *si,
}
smb_llist_constructor(&tree->t_ofile_list, sizeof (smb_ofile_t),
- offsetof(smb_ofile_t, f_lnd));
+ offsetof(smb_ofile_t, f_tree_lnd));
smb_llist_constructor(&tree->t_odir_list, sizeof (smb_odir_t),
offsetof(smb_odir_t, d_lnd));
@@ -968,7 +971,7 @@ smb_tree_alloc(smb_request_t *sr, const smb_kshare_t *si,
* Remove the tree from the user's tree list before freeing resources
* associated with the tree.
*/
-void
+static void
smb_tree_dealloc(void *arg)
{
smb_session_t *session;
@@ -985,6 +988,13 @@ smb_tree_dealloc(void *arg)
atomic_dec_32(&session->s_tree_cnt);
smb_llist_exit(&session->s_tree_list);
+ /*
+ * This tree is no longer on s_tree_list, however...
+ *
+ * This is called via smb_llist_post, which means it may run
+ * BEFORE smb_tree_release drops t_mutex (if another thread
+ * flushes the delete queue before we do). Synchronize.
+ */
mutex_enter(&tree->t_mutex);
mutex_exit(&tree->t_mutex);
@@ -1019,7 +1029,7 @@ smb_tree_is_connected_locked(smb_tree_t *tree)
case SMB_TREE_STATE_DISCONNECTING:
case SMB_TREE_STATE_DISCONNECTED:
/*
- * The tree exists but being diconnected or destroyed.
+ * The tree exists but is being disconnected or destroyed.
*/
return (B_FALSE);
@@ -1030,27 +1040,6 @@ smb_tree_is_connected_locked(smb_tree_t *tree)
}
/*
- * Determine whether or not a tree is disconnected.
- * This function must be called with the tree mutex held.
- */
-static boolean_t
-smb_tree_is_disconnected(smb_tree_t *tree)
-{
- switch (tree->t_state) {
- case SMB_TREE_STATE_DISCONNECTED:
- return (B_TRUE);
-
- case SMB_TREE_STATE_CONNECTED:
- case SMB_TREE_STATE_DISCONNECTING:
- return (B_FALSE);
-
- default:
- ASSERT(0);
- return (B_FALSE);
- }
-}
-
-/*
* Return a pointer to the share name within a share resource path.
*
* The share path may be a Uniform Naming Convention (UNC) string
diff --git a/usr/src/uts/common/fs/smbsrv/smb_user.c b/usr/src/uts/common/fs/smbsrv/smb_user.c
index 046fa00b4d..0bfceb4ff4 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_user.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_user.c
@@ -181,7 +181,9 @@
* Rules of access to a user structure:
*
* 1) In order to avoid deadlocks, when both (mutex and lock of the session
- * list) have to be entered, the lock must be entered first.
+ * list) have to be entered, the lock must be entered first. Additionally,
+ * one may NOT flush the deleteq of either the tree list or the ofile list
+ * while the user mutex is held.
*
* 2) All actions applied to a user require a reference count.
*
@@ -208,23 +210,36 @@
#define ADMINISTRATORS_SID "S-1-5-32-544"
+/* Don't leak object addresses */
+#define SMB_USER_SSNID(u) \
+ ((uintptr_t)&smb_cache_user ^ (uintptr_t)(u))
+
+static void smb_user_delete(void *);
static int smb_user_enum_private(smb_user_t *, smb_svcenum_t *);
static void smb_user_auth_logoff(smb_user_t *);
static void smb_user_logoff_tq(void *);
-
/*
* Create a new user.
+ *
+ * For SMB2 and later, session IDs (u_ssnid) need to be unique among all
+ * current and "recent" sessions. The session ID is derived from the
+ * address of the smb_user object (obscured by XOR with a constant).
+ * This adds a 3-bit generation number in the low bits, incremented
+ * when we allocate an smb_user_t from its kmem cache, so it can't
+ * be confused with a (recent) previous incarnation of this object.
*/
smb_user_t *
smb_user_new(smb_session_t *session)
{
smb_user_t *user;
+ uint_t gen; // generation (low 3 bits of ssnid)
ASSERT(session);
ASSERT(session->s_magic == SMB_SESSION_MAGIC);
user = kmem_cache_alloc(smb_cache_user, KM_SLEEP);
+ gen = (user->u_ssnid + 1) & 7;
bzero(user, sizeof (smb_user_t));
user->u_refcnt = 1;
@@ -234,6 +249,7 @@ smb_user_new(smb_session_t *session)
if (smb_idpool_alloc(&session->s_uid_pool, &user->u_uid))
goto errout;
+ user->u_ssnid = SMB_USER_SSNID(user) + gen;
mutex_init(&user->u_mutex, NULL, MUTEX_DEFAULT, NULL);
user->u_state = SMB_USER_STATE_LOGGING_ON;
@@ -317,8 +333,11 @@ smb_user_logon(
/*
* smb_user_logoff
*
- * Change the user state and disconnect trees.
+ * Change the user state to "logging off" and disconnect trees.
* The user list must not be entered or modified here.
+ *
+ * We remain in state "logging off" until the last ref. is gone,
+ * then smb_user_release takes us to state "logged off".
*/
void
smb_user_logoff(
@@ -337,8 +356,15 @@ smb_user_logoff(
user->u_authsock = NULL;
tmo = user->u_auth_tmo;
user->u_auth_tmo = NULL;
- user->u_state = SMB_USER_STATE_LOGGED_OFF;
- smb_server_dec_users(user->u_server);
+ user->u_state = SMB_USER_STATE_LOGGING_OFF;
+ mutex_exit(&user->u_mutex);
+
+ /* Timeout callback takes u_mutex. See untimeout(9f) */
+ if (tmo != NULL)
+ (void) untimeout(tmo);
+ /* This close can block, so not under the mutex. */
+ if (authsock != NULL)
+ smb_authsock_close(user, authsock);
break;
case SMB_USER_STATE_LOGGED_ON:
@@ -350,29 +376,18 @@ smb_user_logoff(
mutex_exit(&user->u_mutex);
smb_session_disconnect_owned_trees(user->u_session, user);
smb_user_auth_logoff(user);
- mutex_enter(&user->u_mutex);
- user->u_state = SMB_USER_STATE_LOGGED_OFF;
- smb_server_dec_users(user->u_server);
break;
case SMB_USER_STATE_LOGGED_OFF:
case SMB_USER_STATE_LOGGING_OFF:
+ mutex_exit(&user->u_mutex);
break;
default:
ASSERT(0);
+ mutex_exit(&user->u_mutex);
break;
}
- mutex_exit(&user->u_mutex);
-
- /* Timeout callback takes u_mutex. See untimeout(9f) */
- if (tmo != NULL)
- (void) untimeout(tmo);
-
- /* This close can block, so not under the mutex. */
- if (authsock != NULL) {
- smb_authsock_close(user, authsock);
- }
}
/*
@@ -419,23 +434,32 @@ void
smb_user_release(
smb_user_t *user)
{
- ASSERT(user->u_magic == SMB_USER_MAGIC);
+ smb_session_t *ssn = user->u_session;
+
+ SMB_USER_VALID(user);
+
+ /* flush the tree list delete queue */
+ smb_llist_flush(&ssn->s_tree_list);
mutex_enter(&user->u_mutex);
ASSERT(user->u_refcnt);
user->u_refcnt--;
switch (user->u_state) {
- case SMB_USER_STATE_LOGGED_OFF:
- if (user->u_refcnt == 0)
- smb_session_post_user(user->u_session, user);
+ case SMB_USER_STATE_LOGGING_OFF:
+ if (user->u_refcnt == 0) {
+ smb_session_t *ssn = user->u_session;
+ user->u_state = SMB_USER_STATE_LOGGED_OFF;
+ smb_llist_post(&ssn->s_user_list, user,
+ smb_user_delete);
+ }
break;
case SMB_USER_STATE_LOGGING_ON:
case SMB_USER_STATE_LOGGED_ON:
- case SMB_USER_STATE_LOGGING_OFF:
break;
+ case SMB_USER_STATE_LOGGED_OFF:
default:
ASSERT(0);
break;
@@ -626,11 +650,12 @@ smb_user_enum(smb_user_t *user, smb_svcenum_t *svcenum)
* Remove the user from the session's user list before freeing resources
* associated with the user.
*/
-void
+static void
smb_user_delete(void *arg)
{
smb_session_t *session;
smb_user_t *user = (smb_user_t *)arg;
+ uint32_t ucount;
SMB_USER_VALID(user);
ASSERT(user->u_refcnt == 0);
@@ -639,11 +664,28 @@ smb_user_delete(void *arg)
ASSERT(user->u_auth_tmo == NULL);
session = user->u_session;
+
+ smb_server_dec_users(session->s_server);
smb_llist_enter(&session->s_user_list, RW_WRITER);
smb_llist_remove(&session->s_user_list, user);
smb_idpool_free(&session->s_uid_pool, user->u_uid);
+ ucount = smb_llist_get_count(&session->s_user_list);
smb_llist_exit(&session->s_user_list);
+ if (ucount == 0) {
+ smb_rwx_rwenter(&session->s_lock, RW_WRITER);
+ session->s_state = SMB_SESSION_STATE_SHUTDOWN;
+ smb_rwx_cvbcast(&session->s_lock);
+ smb_rwx_rwexit(&session->s_lock);
+ }
+
+ /*
+ * This user is no longer on s_user_list, however...
+ *
+ * This is called via smb_llist_post, which means it may run
+ * BEFORE smb_user_release drops u_mutex (if another thread
+ * flushes the delete queue before we do). Synchronize.
+ */
mutex_enter(&user->u_mutex);
mutex_exit(&user->u_mutex);
@@ -777,7 +819,6 @@ smb_user_netinfo_init(smb_user_t *user, smb_netuserinfo_t *info)
info->ui_native_os = session->native_os;
info->ui_ipaddr = session->ipaddr;
info->ui_numopens = session->s_file_cnt;
- info->ui_smb_uid = user->u_uid;
info->ui_logon_time = user->u_logon_time;
info->ui_flags = user->u_flags;
info->ui_posix_uid = crgetuid(user->u_cred);
@@ -811,11 +852,34 @@ smb_user_netinfo_fini(smb_netuserinfo_t *info)
bzero(info, sizeof (smb_netuserinfo_t));
}
+/*
+ * Tell smbd this user is going away so it can clean up their
+ * audit session, autohome dir, etc.
+ *
+ * Note that when we're shutting down, smbd will already have set
+ * smbd.s_shutting_down and therefore will ignore door calls.
+ * Skip this during shutdown to reduce upcall noise.
+ */
static void
smb_user_auth_logoff(smb_user_t *user)
{
- uint32_t audit_sid = user->u_audit_sid;
+ smb_server_t *sv = user->u_server;
+ uint32_t audit_sid;
+
+ if (sv->sv_state != SMB_SERVER_STATE_RUNNING)
+ return;
- (void) smb_kdoor_upcall(user->u_server, SMB_DR_USER_AUTH_LOGOFF,
+ audit_sid = user->u_audit_sid;
+ (void) smb_kdoor_upcall(sv, SMB_DR_USER_AUTH_LOGOFF,
&audit_sid, xdr_uint32_t, NULL, NULL);
}
+
+boolean_t
+smb_is_same_user(cred_t *cr1, cred_t *cr2)
+{
+ ksid_t *ks1 = crgetsid(cr1, KSID_USER);
+ ksid_t *ks2 = crgetsid(cr2, KSID_USER);
+
+ return (ks1->ks_rid == ks2->ks_rid &&
+ strcmp(ks1->ks_domain->kd_name, ks2->ks_domain->kd_name) == 0);
+}
diff --git a/usr/src/uts/common/smbsrv/smb2.h b/usr/src/uts/common/smbsrv/smb2.h
index 38dfdd5573..225d3afb15 100644
--- a/usr/src/uts/common/smbsrv/smb2.h
+++ b/usr/src/uts/common/smbsrv/smb2.h
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SMB_SMB2_H
@@ -262,6 +262,22 @@ typedef enum {
* on disk. No data is passed to the server by the client.
*/
+#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 0x44483251 /* ("DH2Q") */
+/*
+ * The client is requesting the open to be durable.
+ * This value is only supported for the SMB 3.x dialect family.
+ */
+
+#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 0x44483243 /* ("DH2C") */
+/*
+ * The client is requesting to reconnect to a
+ * durable open after being disconnected.
+ * This value is only supported for the SMB 3.x dialect family.
+ */
+
+#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002
+/* A persistent handle is requested. */
+
#define SMB2_CREATE_REQUEST_LEASE 0x52714c73 /* ("RqLs") */
/*
* The client is requesting that the server return a lease.
diff --git a/usr/src/uts/common/smbsrv/smb2_kproto.h b/usr/src/uts/common/smbsrv/smb2_kproto.h
index b408222890..18ed5c5259 100644
--- a/usr/src/uts/common/smbsrv/smb2_kproto.h
+++ b/usr/src/uts/common/smbsrv/smb2_kproto.h
@@ -28,6 +28,11 @@ extern uint32_t smb2_max_rwsize;
extern uint32_t smb2_max_trans;
extern int smb2_aapl_use_file_ids;
+extern uint32_t smb2_dh_def_timeout;
+extern uint32_t smb2_dh_max_timeout;
+extern uint32_t smb2_res_def_timeout;
+extern uint32_t smb2_res_max_timeout;
+extern int smb2_enable_dh;
void smb2_dispatch_stats_init(smb_server_t *);
void smb2_dispatch_stats_fini(smb_server_t *);
@@ -51,6 +56,7 @@ void smb2_sign_reply(smb_request_t *);
void smb2_sign_init_mech(smb_session_t *);
uint32_t smb2_fsctl_vneginfo(smb_request_t *, smb_fsctl_t *);
+uint32_t smb2_fsctl_resiliency(smb_request_t *, smb_fsctl_t *);
smb_sdrc_t smb2_negotiate(smb_request_t *);
smb_sdrc_t smb2_session_setup(smb_request_t *);
@@ -93,6 +99,12 @@ uint32_t smb2_setinfo_fs(smb_request_t *, smb_setinfo_t *, int);
uint32_t smb2_setinfo_sec(smb_request_t *, smb_setinfo_t *, uint32_t);
uint32_t smb2_setinfo_quota(smb_request_t *, smb_setinfo_t *);
+void smb2_durable_timers(smb_server_t *);
+
+uint32_t smb2_dh_reconnect(smb_request_t *);
+boolean_t smb_dh_should_save(smb_ofile_t *);
+extern void smb2_dh_shutdown(smb_server_t *);
+
void smb2sr_finish_async(smb_request_t *);
#ifdef __cplusplus
diff --git a/usr/src/uts/common/smbsrv/smb_kproto.h b/usr/src/uts/common/smbsrv/smb_kproto.h
index 05e081df37..61d2b7b42e 100644
--- a/usr/src/uts/common/smbsrv/smb_kproto.h
+++ b/usr/src/uts/common/smbsrv/smb_kproto.h
@@ -254,7 +254,6 @@ uint32_t smb_make_link(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
* Logging functions
*/
void smb_log_flush(void);
-void smb_correct_keep_alive_values(uint32_t new_keep_alive);
void smb_close_all_connections(void);
int smb_net_id(uint32_t);
@@ -310,6 +309,7 @@ int smb_search(smb_request_t *);
uint32_t smb_common_create(smb_request_t *);
uint32_t smb_common_open(smb_request_t *);
+
int smb_common_write(smb_request_t *, smb_rw_param_t *);
void smb_pathname_init(smb_request_t *, smb_pathname_t *, char *);
@@ -435,6 +435,8 @@ int smb_server_file_close(smb_ioc_fileid_t *);
int smb_server_sharevp(smb_server_t *, const char *, vnode_t **);
int smb_server_unshare(const char *);
+void smb_server_logoff_ssnid(smb_request_t *, uint64_t);
+
void smb_server_get_cfg(smb_server_t *, smb_kmod_cfg_t *);
int smb_server_spooldoc(smb_ioc_spooldoc_t *);
@@ -587,20 +589,21 @@ void smb_authsock_close(smb_user_t *, ksocket_t);
* session functions (file smb_session.c)
*/
smb_session_t *smb_session_create(ksocket_t, uint16_t, smb_server_t *, int);
+smb_session_t *smb_server_find_session_byptr(smb_server_t *, void *);
+
void smb_session_receiver(smb_session_t *);
void smb_session_disconnect(smb_session_t *);
-void smb_session_timers(smb_llist_t *);
+void smb_session_timers(smb_server_t *);
void smb_session_delete(smb_session_t *session);
void smb_session_cancel_requests(smb_session_t *, smb_tree_t *,
smb_request_t *);
void smb_session_config(smb_session_t *session);
void smb_session_disconnect_from_share(smb_llist_t *, char *);
smb_user_t *smb_session_dup_user(smb_session_t *, char *, char *);
+smb_user_t *smb_session_lookup_ssnid(smb_session_t *, uint64_t);
smb_user_t *smb_session_lookup_uid(smb_session_t *, uint16_t);
-smb_user_t *smb_session_lookup_uid_st(smb_session_t *session,
- uint16_t uid, smb_user_state_t st);
-void smb_session_post_user(smb_session_t *, smb_user_t *);
-void smb_session_post_tree(smb_session_t *, smb_tree_t *);
+smb_user_t *smb_session_lookup_uid_st(smb_session_t *,
+ uint64_t, uint16_t, smb_user_state_t);
smb_tree_t *smb_session_lookup_tree(smb_session_t *, uint16_t);
smb_tree_t *smb_session_lookup_share(smb_session_t *, const char *,
smb_tree_t *);
@@ -608,12 +611,10 @@ smb_tree_t *smb_session_lookup_volume(smb_session_t *, const char *,
smb_tree_t *);
void smb_session_close_pid(smb_session_t *, uint32_t);
void smb_session_disconnect_owned_trees(smb_session_t *, smb_user_t *);
-void smb_session_disconnect_trees(smb_session_t *);
void smb_session_disconnect_share(smb_session_t *, const char *);
void smb_session_getclient(smb_session_t *, char *, size_t);
boolean_t smb_session_isclient(smb_session_t *, const char *);
void smb_session_correct_keep_alive_values(smb_llist_t *, uint32_t);
-void smb_session_oplock_break(smb_request_t *, uint8_t);
int smb_session_send(smb_session_t *, uint8_t type, mbuf_chain_t *);
int smb_session_xprt_gethdr(smb_session_t *, smb_xprt_t *);
boolean_t smb_session_oplocks_enable(smb_session_t *);
@@ -629,19 +630,19 @@ void smb_request_free(smb_request_t *);
*/
smb_ofile_t *smb_ofile_lookup_by_fid(smb_request_t *, uint16_t);
smb_ofile_t *smb_ofile_lookup_by_uniqid(smb_tree_t *, uint32_t);
+smb_ofile_t *smb_ofile_lookup_by_persistid(smb_request_t *, uint64_t);
boolean_t smb_ofile_disallow_fclose(smb_ofile_t *);
smb_ofile_t *smb_ofile_open(smb_request_t *, smb_node_t *,
smb_arg_open_t *, uint16_t, uint32_t, smb_error_t *);
void smb_ofile_close(smb_ofile_t *, int32_t);
-void smb_ofile_delete(void *);
uint32_t smb_ofile_access(smb_ofile_t *, cred_t *, uint32_t);
int smb_ofile_seek(smb_ofile_t *, ushort_t, int32_t, uint32_t *);
void smb_ofile_flush(smb_request_t *, smb_ofile_t *);
boolean_t smb_ofile_hold(smb_ofile_t *);
+boolean_t smb_ofile_hold_olbrk(smb_ofile_t *);
void smb_ofile_release(smb_ofile_t *);
+void smb_ofile_close_all(smb_tree_t *, uint32_t);
void smb_ofile_request_complete(smb_ofile_t *);
-void smb_ofile_close_all(smb_tree_t *);
-void smb_ofile_close_all_by_pid(smb_tree_t *, uint16_t);
void smb_ofile_set_flags(smb_ofile_t *, uint32_t);
boolean_t smb_ofile_is_open(smb_ofile_t *);
int smb_ofile_enum(smb_ofile_t *, smb_svcenum_t *);
@@ -654,6 +655,8 @@ void smb_ofile_set_delete_on_close(smb_ofile_t *);
void smb_delayed_write_timer(smb_llist_t *);
void smb_ofile_set_quota_resume(smb_ofile_t *, char *);
void smb_ofile_get_quota_resume(smb_ofile_t *, char *, int);
+void smb_ofile_del_persistid(smb_ofile_t *);
+void smb_ofile_set_persistid(smb_ofile_t *);
#define SMB_OFILE_GET_SESSION(of) ((of)->f_session)
#define SMB_OFILE_GET_TREE(of) ((of)->f_tree)
@@ -674,7 +677,6 @@ void smb_odir_reopen(smb_odir_t *, const char *, uint16_t);
void smb_odir_close(smb_odir_t *);
boolean_t smb_odir_hold(smb_odir_t *);
void smb_odir_release(smb_odir_t *);
-void smb_odir_delete(void *);
int smb_odir_read(smb_request_t *, smb_odir_t *,
smb_odirent_t *, boolean_t *);
@@ -695,7 +697,6 @@ smb_user_t *smb_user_new(smb_session_t *);
int smb_user_logon(smb_user_t *, cred_t *,
char *, char *, uint32_t, uint32_t, uint32_t);
void smb_user_logoff(smb_user_t *);
-void smb_user_delete(void *);
void smb_user_auth_tmo(void *);
boolean_t smb_user_is_admin(smb_user_t *);
@@ -712,15 +713,13 @@ int smb_user_netinfo_encode(smb_user_t *, uint8_t *, size_t, uint32_t *);
smb_token_t *smb_get_token(smb_session_t *, smb_logon_t *);
cred_t *smb_cred_create(smb_token_t *);
void smb_user_setcred(smb_user_t *, cred_t *, uint32_t);
+boolean_t smb_is_same_user(cred_t *, cred_t *);
/*
* SMB tree functions (file smb_tree.c)
*/
uint32_t smb_tree_connect(smb_request_t *);
void smb_tree_disconnect(smb_tree_t *, boolean_t);
-void smb_tree_dealloc(void *);
-void smb_tree_post_ofile(smb_tree_t *, smb_ofile_t *);
-void smb_tree_post_odir(smb_tree_t *, smb_odir_t *);
void smb_tree_close_pid(smb_tree_t *, uint32_t);
boolean_t smb_tree_has_feature(smb_tree_t *, uint_t);
int smb_tree_enum(smb_tree_t *, smb_svcenum_t *);
@@ -730,7 +729,6 @@ void smb_tree_hold_internal(smb_tree_t *);
void smb_tree_release(smb_tree_t *);
smb_odir_t *smb_tree_lookup_odir(smb_request_t *, uint16_t);
boolean_t smb_tree_is_connected(smb_tree_t *);
-#define SMB_TREE_GET_TID(tree) ((tree)->t_tid)
smb_xa_t *smb_xa_create(smb_session_t *session, smb_request_t *sr,
uint32_t total_parameter_count, uint32_t total_data_count,
@@ -774,6 +772,7 @@ 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 *, 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 *);
@@ -782,7 +781,6 @@ 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_enter(ll, mode) rw_enter(&(ll)->ll_lock, mode)
#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);
@@ -796,22 +794,19 @@ void smb_slist_insert_head(smb_slist_t *sl, void *obj);
void smb_slist_insert_tail(smb_slist_t *sl, void *obj);
void smb_slist_remove(smb_slist_t *sl, void *obj);
void smb_slist_wait_for_empty(smb_slist_t *sl);
+void smb_slist_enter(smb_slist_t *sl);
void smb_slist_exit(smb_slist_t *sl);
uint32_t smb_slist_move_tail(list_t *lst, smb_slist_t *sl);
void smb_slist_obj_move(smb_slist_t *dst, smb_slist_t *src, void *obj);
-#define smb_slist_enter(sl) mutex_enter(&(sl)->sl_mutex)
#define smb_slist_head(sl) list_head(&(sl)->sl_list)
#define smb_slist_next(sl, obj) list_next(&(sl)->sl_list, obj)
void smb_rwx_init(smb_rwx_t *rwx);
void smb_rwx_destroy(smb_rwx_t *rwx);
-#define smb_rwx_rwenter(rwx, mode) rw_enter(&(rwx)->rwx_lock, mode)
+void smb_rwx_rwenter(smb_rwx_t *rwx, krw_t);
void smb_rwx_rwexit(smb_rwx_t *rwx);
-int smb_rwx_rwwait(smb_rwx_t *rwx, clock_t timeout);
-#define smb_rwx_xenter(rwx) mutex_enter(&(rwx)->rwx_mutex)
-#define smb_rwx_xexit(rwx) mutex_exit(&(rwx)->rwx_mutex)
-krw_t smb_rwx_rwupgrade(smb_rwx_t *rwx);
-void smb_rwx_rwdowngrade(smb_rwx_t *rwx, krw_t mode);
+int smb_rwx_cvwait(smb_rwx_t *rwx, clock_t timeout);
+void smb_rwx_cvbcast(smb_rwx_t *rwx);
void smb_thread_init(smb_thread_t *, char *, smb_thread_ep_t,
void *, pri_t);
@@ -919,6 +914,11 @@ int smb_threshold_enter(smb_cmd_threshold_t *);
void smb_threshold_exit(smb_cmd_threshold_t *);
void smb_threshold_wake_all(smb_cmd_threshold_t *);
+/* SMB hash function prototypes */
+smb_hash_t *smb_hash_create(size_t, size_t, uint32_t num_buckets);
+void smb_hash_destroy(smb_hash_t *);
+uint_t smb_hash_uint64(smb_hash_t *, uint64_t);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/smbsrv/smb_ktypes.h b/usr/src/uts/common/smbsrv/smb_ktypes.h
index 6195dbd9fd..2bf62f6242 100644
--- a/usr/src/uts/common/smbsrv/smb_ktypes.h
+++ b/usr/src/uts/common/smbsrv/smb_ktypes.h
@@ -391,6 +391,17 @@ typedef struct smb_llist {
boolean_t ll_flushing;
} smb_llist_t;
+typedef struct smb_bucket {
+ smb_llist_t b_list;
+ uint32_t b_max_seen;
+} smb_bucket_t;
+
+typedef struct smb_hash {
+ uint32_t rshift;
+ uint32_t num_buckets;
+ smb_bucket_t *buckets;
+} smb_hash_t;
+
typedef struct smb_slist {
kmutex_t sl_mutex;
kcondvar_t sl_cv;
@@ -613,6 +624,7 @@ typedef struct smb_oplock_grant {
uint32_t og_magic;
uint8_t og_breaking;
uint8_t og_level;
+ uint8_t og_dialect; /* how to send breaks */
struct smb_ofile *og_ofile;
} smb_oplock_grant_t;
@@ -812,15 +824,39 @@ struct smb_key {
#define SMB_SIGNING_CHECK 2
/*
+ * Locking notes:
+ * If you hold the mutex/lock on an object, don't flush the deleteq
+ * of the objects directly below it in the logical hierarchy
+ * (i.e. via smb_llist_exit()). I.e. don't drop s_tree_list when
+ * you hold u_mutex, because deleted trees need u_mutex to
+ * lower the refcnt.
+ *
+ * Note that this also applies to u_mutex and t_ofile_list.
+ */
+
+/*
+ * The "session" object.
+ *
+ * Note that the smb_session_t object here corresponds to what MS-SMB2
+ * calls a "connection". Adding to the confusion, what MS calls a
+ * "session" corresponds to our smb_user_t (below).
+ */
+
+/*
* Session State Machine
* ---------------------
*
- * +-----------------------------+ +------------------------------+
- * | SMB_SESSION_STATE_CONNECTED | | SMB_SESSION_STATE_TERMINATED |
- * +-----------------------------+ +------------------------------+
- * T0| ^
- * +--------------------+ |T5
- * v |T4 |
+ *
+ * +-----------------------------+ +----------------------------+
+ * | SMB_SESSION_STATE_CONNECTED | | SMB_SESSION_STATE_SHUTDOWN |
+ * +-----------------------------+ +----------------------------+
+ * | ^
+ * | |T6
+ * | +------------------------------+
+ * | | SMB_SESSION_STATE_TERMINATED |
+ * T0| +------------------------------+
+ * +--------------------+ ^
+ * v |T4 |T5
* +-------------------------------+ | +--------------------------------+
* | SMB_SESSION_STATE_ESTABLISHED |---+--->| SMB_SESSION_STATE_DISCONNECTED |
* +-------------------------------+ +--------------------------------+
@@ -856,6 +892,10 @@ struct smb_key {
*
*
*
+ * Transition T6
+ *
+ *
+ *
*/
#define SMB_SESSION_MAGIC 0x53455353 /* 'SESS' */
#define SMB_SESSION_VALID(p) \
@@ -870,6 +910,7 @@ typedef enum {
SMB_SESSION_STATE_ESTABLISHED,
SMB_SESSION_STATE_NEGOTIATED,
SMB_SESSION_STATE_TERMINATED,
+ SMB_SESSION_STATE_SHUTDOWN,
SMB_SESSION_STATE_SENTINEL
} smb_session_state_t;
@@ -934,6 +975,7 @@ typedef struct smb_session {
uint32_t challenge_len;
unsigned char challenge_key[SMB_CHALLENGE_SZ];
int64_t activity_timestamp;
+
/*
* Maximum negotiated buffer sizes between SMB client and server
* in SMB_SESSION_SETUP_ANDX
@@ -946,10 +988,17 @@ typedef struct smb_session {
uint64_t start_time;
unsigned char MAC_key[44];
char ip_addr_str[INET6_ADDRSTRLEN];
- char clnt_uuid[16];
+ uint8_t clnt_uuid[16];
char workstation[SMB_PI_MAX_HOST];
} smb_session_t;
+/*
+ * The "user" object.
+ *
+ * Note that smb_user_t object here corresponds to what MS-SMB2 calls
+ * a "session". (Our smb_session_t is something else -- see above).
+ */
+
#define SMB_USER_MAGIC 0x55534552 /* 'USER' */
#define SMB_USER_VALID(u) \
ASSERT(((u) != NULL) && ((u)->u_magic == SMB_USER_MAGIC))
@@ -968,7 +1017,9 @@ typedef struct smb_session {
#define SMB_USER_PRIV_RESTORE 0x00000004
#define SMB_USER_PRIV_SECURITY 0x00000008
-
+/*
+ * See the long "User State Machine" comment in smb_user.c
+ */
typedef enum {
SMB_USER_STATE_LOGGING_ON = 0,
SMB_USER_STATE_LOGGED_ON,
@@ -977,6 +1028,12 @@ typedef enum {
SMB_USER_STATE_SENTINEL
} smb_user_state_t;
+typedef enum {
+ SMB2_DH_PRESERVE_NONE = 0,
+ SMB2_DH_PRESERVE_SOME,
+ SMB2_DH_PRESERVE_ALL
+} smb_preserve_type_t;
+
typedef struct smb_user {
list_node_t u_lnd;
uint32_t u_magic;
@@ -995,10 +1052,12 @@ typedef struct smb_user {
cred_t *u_cred;
cred_t *u_privcred;
+ uint64_t u_ssnid; /* unique server-wide */
uint32_t u_refcnt;
uint32_t u_flags;
+ smb_preserve_type_t preserve_opens;
uint32_t u_privileges;
- uint16_t u_uid;
+ uint16_t u_uid; /* unique per-session */
uint32_t u_audit_sid;
uint32_t u_sign_flags;
@@ -1033,6 +1092,9 @@ typedef struct smb_user {
#define SMB_TREE_SPARSE 0x00040000
#define SMB_TREE_TRAVERSE_MOUNTS 0x00080000
+/*
+ * See the long "Tree State Machine" comment in smb_tree.c
+ */
typedef enum {
SMB_TREE_STATE_CONNECTED = 0,
SMB_TREE_STATE_DISCONNECTING,
@@ -1263,16 +1325,33 @@ typedef struct smb_opipe {
#define SMB_OFILE_VALID(p) \
ASSERT((p != NULL) && ((p)->f_magic == SMB_OFILE_MAGIC))
+/* {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_OPEN = 0,
+ 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_lnd; /* t_ofile_list */
- list_node_t f_nnd; /* n_ofile_list */
+ 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;
@@ -1285,6 +1364,13 @@ typedef struct smb_ofile {
smb_odir_t *f_odir;
smb_opipe_t *f_pipe;
+ kcondvar_t f_cv;
+ /*
+ * Note: f_persistid == 0 means this ofile has no persistid
+ * (same interpretation at the protocol level). IFF non-zero,
+ * this ofile is linked in the sv_persistid_ht hash table.
+ */
+ uint64_t f_persistid;
uint32_t f_uniqid;
uint32_t f_refcnt;
uint64_t f_seek_pos;
@@ -1304,6 +1390,12 @@ typedef struct smb_ofile {
char f_quota_resume[SMB_SID_STRSZ];
smb_oplock_grant_t f_oplock_grant;
smb_notify_t f_notify;
+
+ smb_dh_vers_t dh_vers;
+ hrtime_t dh_timeout_offset; /* time offset for timeout */
+ hrtime_t dh_expire_time; /* time the handle expires */
+ boolean_t dh_persist;
+ uint8_t dh_create_guid[16];
} smb_ofile_t;
typedef struct smb_fileinfo {
@@ -1501,6 +1593,11 @@ typedef struct open_param {
void *create_ctx;
uint8_t op_oplock_level; /* requested/granted level */
boolean_t op_oplock_levelII; /* TRUE if levelII supported */
+ smb_dh_vers_t dh_vers;
+ smb2fid_t dh_fileid; /* for durable reconnect */
+ uint8_t create_guid[16];
+ uint32_t dh_v2_flags;
+ uint32_t dh_timeout;
} smb_arg_open_t;
typedef struct smb_arg_lock {
@@ -1692,7 +1789,7 @@ typedef struct smb_request {
unsigned char smb_sig[8]; /* signiture */
uint16_t smb_tid; /* tree id # */
uint32_t smb_pid; /* caller's process id # */
- uint16_t smb_uid; /* user id # */
+ uint16_t smb_uid; /* local (smb1) user id # */
uint16_t smb_mid; /* mutiplex id # */
unsigned char smb_wct; /* count of parameter words */
uint16_t smb_bcc; /* data byte count */
@@ -1722,8 +1819,8 @@ typedef struct smb_request {
uint64_t smb2_first_msgid;
/* uint32_t smb2_pid; use smb_pid */
/* uint32_t smb2_tid; use smb_tid */
- /* uint64_t smb2_ssnid; use smb_uid */
- unsigned char smb2_sig[16]; /* signiture */
+ uint64_t smb2_ssnid; /* See u_ssnid */
+ uint8_t smb2_sig[16]; /* signature */
uint64_t smb2_async_id;
struct smb2_async_req *sr_async_req;
@@ -1890,7 +1987,6 @@ typedef struct {
int ld_family;
struct sockaddr_in ld_sin;
struct sockaddr_in6 ld_sin6;
- smb_llist_t ld_session_list;
} smb_listener_daemon_t;
#define SMB_SSETUP_CMD "authentication"
@@ -1951,6 +2047,8 @@ typedef struct smb_server {
krwlock_t sv_cfg_lock;
smb_kmod_cfg_t sv_cfg;
smb_session_t *sv_session;
+ smb_llist_t sv_session_list;
+ smb_hash_t *sv_persistid_ht;
struct smb_export sv_export;
struct __door_handle *sv_lmshrd;
diff --git a/usr/src/uts/common/smbsrv/smb_xdr.h b/usr/src/uts/common/smbsrv/smb_xdr.h
index aaf0ff070f..1ea2a008b8 100644
--- a/usr/src/uts/common/smbsrv/smb_xdr.h
+++ b/usr/src/uts/common/smbsrv/smb_xdr.h
@@ -127,7 +127,6 @@ typedef struct smb_doorhdr {
*/
typedef struct smb_netuserinfo {
uint64_t ui_session_id;
- uint16_t ui_smb_uid;
uint16_t ui_domain_len;
char *ui_domain;
uint16_t ui_account_len;
diff --git a/usr/src/uts/common/sys/bitmap.h b/usr/src/uts/common/sys/bitmap.h
index 02d54d2bb5..5e6385811f 100644
--- a/usr/src/uts/common/sys/bitmap.h
+++ b/usr/src/uts/common/sys/bitmap.h
@@ -26,6 +26,7 @@
/*
* Copyright (c) 2014 by Delphix. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright 2017 RackTop Systems.
*/