summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c200
-rw-r--r--usr/src/cmd/smbsrv/smbd/server.xml10
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd.h4
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c4
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd_main.c7
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd_vss.c27
-rw-r--r--usr/src/cmd/smbsrv/smbstat/smbstat.c201
-rw-r--r--usr/src/common/smbclnt/smb_status2winerr.c744
-rw-r--r--usr/src/common/smbclnt/smb_status2winerr.h37
-rw-r--r--usr/src/common/smbsrv/smb_xdr.c5
-rw-r--r--usr/src/lib/libfakekernel/common/clock.c10
-rw-r--r--usr/src/lib/libfakekernel/common/ksocket.c32
-rw-r--r--usr/src/lib/libfakekernel/common/mapfile-vers3
-rw-r--r--usr/src/lib/libfakekernel/common/uio.c27
-rw-r--r--usr/src/lib/libshare/smb/libshare_smb.c26
-rw-r--r--usr/src/lib/smbsrv/libfksmbsrv/Makefile.com45
-rw-r--r--usr/src/lib/smbsrv/libfksmbsrv/common/fake_vop.c33
-rw-r--r--usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_dt.c58
-rw-r--r--usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_init.c2
-rw-r--r--usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_sign_pkcs.c76
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/smb_quota.c8
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/libsmb.h9
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/mapfile-vers4
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_cfg.c173
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_info.c77
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_kmod.c5
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c62
-rw-r--r--usr/src/man/man4/smb.415
-rw-r--r--usr/src/uts/common/Makefile.files53
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_cancel.c98
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_change_notify.c147
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_close.c90
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_create.c668
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_dispatch.c1269
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_echo.c50
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_flush.c72
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_ioctl.c264
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_lock.c293
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_logoff.c54
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_negotiate.c377
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_ofile.c110
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_oplock.c152
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c578
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_qinfo_fs.c289
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_qinfo_quota.c121
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_qinfo_sec.c94
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_query_dir.c567
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_query_info.c147
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_read.c153
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_session_setup.c175
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_set_info.c120
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_setinfo_file.c290
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_setinfo_fs.c91
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_setinfo_quota.c89
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_setinfo_sec.c80
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_signing.c325
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c113
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_tree_disconn.c56
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_write.c165
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_authenticate.c8
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_cmn_rename.c692
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_cmn_setfile.c231
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_common_open.c158
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_common_transact.c89
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_create.c4
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_delete.c22
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_dfs.c541
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_dispatch.c273
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_echo.c10
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_errno.c126
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_find.c60
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_fsinfo.c72
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_fsops.c118
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_init.c7
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_kshare.c2
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_kutil.c30
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_lock.c7
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_locking_andx.c69
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c78
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_mbuf_util.c175
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_negotiate.c169
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_net.c184
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_node.c12
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_notify.c422
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c6
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c12
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c36
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c392
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_nt_transact_quota.c456
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_nt_transact_security.c104
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_odir.c237
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_ofile.c42
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_open_andx.c24
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_opipe.c139
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_oplock.c321
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_pathname.c52
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c91
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_quota.c465
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_rename.c663
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_sd.c13
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_server.c46
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_session.c380
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_set_fileinfo.c441
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_sign_kcf.c83
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_trans2_dfs.c488
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_trans2_find.c60
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_tree.c247
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_tree_connect.c51
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_vops.c11
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_vss.c86
-rw-r--r--usr/src/uts/common/smb/ntstatus.h2
-rw-r--r--usr/src/uts/common/smbsrv/Makefile2
-rw-r--r--usr/src/uts/common/smbsrv/mbuf.h39
-rw-r--r--usr/src/uts/common/smbsrv/netbios.h7
-rw-r--r--usr/src/uts/common/smbsrv/smb.h29
-rw-r--r--usr/src/uts/common/smbsrv/smb2.h371
-rw-r--r--usr/src/uts/common/smbsrv/smb2_kproto.h95
-rw-r--r--usr/src/uts/common/smbsrv/smb_dfs.h14
-rw-r--r--usr/src/uts/common/smbsrv/smb_door.h4
-rw-r--r--usr/src/uts/common/smbsrv/smb_fsops.h9
-rw-r--r--usr/src/uts/common/smbsrv/smb_inet.h6
-rw-r--r--usr/src/uts/common/smbsrv/smb_ioctl.h5
-rw-r--r--usr/src/uts/common/smbsrv/smb_kproto.h183
-rw-r--r--usr/src/uts/common/smbsrv/smb_kstat.h5
-rw-r--r--usr/src/uts/common/smbsrv/smb_ktypes.h435
-rw-r--r--usr/src/uts/common/smbsrv/smb_privilege.h16
-rw-r--r--usr/src/uts/common/smbsrv/smb_sid.h7
-rw-r--r--usr/src/uts/common/smbsrv/smb_signing.h23
-rw-r--r--usr/src/uts/common/smbsrv/smb_token.h7
-rw-r--r--usr/src/uts/common/smbsrv/smb_vops.h1
-rw-r--r--usr/src/uts/common/smbsrv/smb_xdr.h57
-rw-r--r--usr/src/uts/common/smbsrv/smbinfo.h30
-rw-r--r--usr/src/uts/common/smbsrv/winioctl.h73
-rw-r--r--usr/src/uts/intel/smbsrv/Makefile5
-rw-r--r--usr/src/uts/sparc/smbsrv/Makefile5
135 files changed, 14776 insertions, 4213 deletions
diff --git a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c
index 113f5bf458..a3e86379be 100644
--- a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c
+++ b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.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 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <mdb/mdb_modapi.h>
@@ -363,6 +363,29 @@ static smb_com_entry_t smb_com[256] =
SMB_COM_ENTRY(0xFF, "?")
};
+static const char *smb2_cmd_names[SMB2__NCMDS] = {
+ "smb2_negotiate",
+ "smb2_session_setup",
+ "smb2_logoff",
+ "smb2_tree_connect",
+ "smb2_tree_disconn",
+ "smb2_create",
+ "smb2_close",
+ "smb2_flush",
+ "smb2_read",
+ "smb2_write",
+ "smb2_lock",
+ "smb2_ioctl",
+ "smb2_cancel",
+ "smb2_echo",
+ "smb2_query_dir",
+ "smb2_change_notify",
+ "smb2_query_info",
+ "smb2_set_info",
+ "smb2_oplock_break",
+ "smb2_invalid_cmd"
+};
+
static int smb_dcmd_list(uintptr_t, uint_t, int, const mdb_arg_t *);
static void smb_dcmd_list_help(void);
static int smb_dcmd_server(uintptr_t, uint_t, int, const mdb_arg_t *);
@@ -682,7 +705,6 @@ static const char *smb_session_state[SMB_SESSION_STATE_SENTINEL] =
"CONNECTED",
"ESTABLISHED",
"NEGOTIATED",
- "OPLOCK_BREAKING",
"TERMINATED"
};
@@ -740,6 +762,9 @@ smb_dcmd_session(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
if (((opts & SMB_OPT_WALK) && (opts & SMB_OPT_SESSION)) ||
!(opts & SMB_OPT_WALK)) {
+ char cipaddr[INET6_ADDRSTRLEN];
+ char lipaddr[INET6_ADDRSTRLEN];
+ int ipaddrstrlen;
smb_session_t *se;
const char *state;
@@ -755,31 +780,42 @@ smb_dcmd_session(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
else
state = smb_session_state[se->s_state];
+ switch (se->ipaddr.a_family) {
+ case AF_INET:
+ ipaddrstrlen = INET_ADDRSTRLEN;
+ (void) mdb_snprintf(cipaddr, sizeof (cipaddr),
+ "%I", se->ipaddr.a_ipv4);
+ (void) mdb_snprintf(lipaddr, sizeof (lipaddr),
+ "%I", se->local_ipaddr.a_ipv4);
+ break;
+ case AF_INET6:
+ ipaddrstrlen = INET6_ADDRSTRLEN;
+ (void) mdb_snprintf(cipaddr, sizeof (cipaddr),
+ "%N", &(se->ipaddr.a_ipv6));
+ (void) mdb_snprintf(lipaddr, sizeof (lipaddr),
+ "%N", &(se->local_ipaddr.a_ipv6));
+ break;
+ default:
+ ipaddrstrlen = INET_ADDRSTRLEN;
+ (void) mdb_snprintf(cipaddr, sizeof (cipaddr),
+ "unknown");
+ (void) mdb_snprintf(lipaddr, sizeof (lipaddr),
+ "unknown");
+ }
+
if (opts & SMB_OPT_VERBOSE) {
mdb_printf("%<b>%<u>SMB session information "
"(%p): %</u>%</b>\n", addr);
- switch (se->ipaddr.a_family) {
- case AF_INET:
- mdb_printf("Client IP address: %I\n",
- se->ipaddr.a_ipv4);
- mdb_printf("Local IP Address: %I\n",
- se->local_ipaddr.a_ipv4);
- break;
- case AF_INET6:
- mdb_printf("Client IP address: %N\n",
- &(se->ipaddr.a_ipv6));
- mdb_printf("Local IP Address: %N\n",
- &(se->local_ipaddr.a_ipv6));
- break;
- default:
- mdb_printf("Client IP address: unknown\n");
- mdb_printf("Local IP Address: unknown\n");
- }
+ mdb_printf("Client IP address: %s %d\n",
+ cipaddr, se->s_remote_port);
+ mdb_printf("Local IP Address: %s %d\n",
+ lipaddr, se->s_local_port);
mdb_printf("Session KID: %u\n", se->s_kid);
mdb_printf("Workstation Name: %s\n",
se->workstation);
mdb_printf("Session state: %u (%s)\n", se->s_state,
state);
+ mdb_printf("Session dialect: %#x\n", se->dialect);
mdb_printf("Number of Users: %u\n",
se->s_user_list.ll_count);
mdb_printf("Number of Trees: %u\n", se->s_tree_cnt);
@@ -788,41 +824,15 @@ smb_dcmd_session(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
mdb_printf("Number of active Transact.: %u\n\n",
se->s_xa_list.ll_count);
} else {
- char cipaddr[INET6_ADDRSTRLEN];
- char lipaddr[INET6_ADDRSTRLEN];
- int ipaddrstrlen;
-
- switch (se->ipaddr.a_family) {
- case AF_INET:
- ipaddrstrlen = INET_ADDRSTRLEN;
- (void) mdb_snprintf(cipaddr, sizeof (cipaddr),
- "%I", se->ipaddr.a_ipv4);
- (void) mdb_snprintf(lipaddr, sizeof (lipaddr),
- "%I", se->local_ipaddr.a_ipv4);
- break;
- case AF_INET6:
- ipaddrstrlen = INET6_ADDRSTRLEN;
- (void) mdb_snprintf(cipaddr, sizeof (cipaddr),
- "%N", &(se->ipaddr.a_ipv6));
- (void) mdb_snprintf(lipaddr, sizeof (lipaddr),
- "%N", &(se->local_ipaddr.a_ipv6));
- break;
- default:
- ipaddrstrlen = INET_ADDRSTRLEN;
- (void) mdb_snprintf(cipaddr, sizeof (cipaddr),
- "unknown");
- (void) mdb_snprintf(lipaddr, sizeof (lipaddr),
- "unknown");
- }
-
if (DCMD_HDRSPEC(flags)) {
mdb_printf(
- "%<b>%<u>%-?s %-*s %-*s %-16s%</u>%</b>\n",
- "SESSION", ipaddrstrlen, "CLIENT_IP_ADDR",
- ipaddrstrlen, "LOCAL_IP_ADDR", "STATE");
+ "%<b>%<u>%-?s %-*s %-8s %-8s %-12s%</u>%</b>\n",
+ "SESSION", ipaddrstrlen, "IP_ADDR",
+ "PORT", "DIALECT", "STATE");
}
- mdb_printf("%-?p %-*s %-*s %s\n", addr, ipaddrstrlen,
- cipaddr, ipaddrstrlen, lipaddr, state);
+ mdb_printf("%-?p %-*s %-8d %-8#x %s\n",
+ addr, ipaddrstrlen, cipaddr,
+ se->s_remote_port, se->dialect, state);
}
}
if (smb_obj_expand(addr, opts, smb_session_exp, indent))
@@ -873,6 +883,8 @@ smb_dcmd_request(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
!(opts & SMB_OPT_WALK)) {
smb_request_t *sr;
const char *state;
+ const char *cur_cmd_name;
+ uint_t cur_cmd_code;
uint64_t waiting;
uint64_t running;
@@ -911,35 +923,76 @@ smb_dcmd_request(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
else
state = smb_request_state[sr->sr_state];
+ if (sr->smb2_cmd_code != 0) {
+ /* SMB2 request */
+ cur_cmd_code = sr->smb2_cmd_code;
+ if (cur_cmd_code > SMB2_INVALID_CMD)
+ cur_cmd_code = SMB2_INVALID_CMD;
+ cur_cmd_name = smb2_cmd_names[cur_cmd_code];
+ } else {
+ /* SMB1 request */
+ cur_cmd_code = sr->smb_com & 0xFF;
+ cur_cmd_name = smb_com[cur_cmd_code].smb_com;
+ }
+
if (opts & SMB_OPT_VERBOSE) {
mdb_printf(
"%</b>%</u>SMB request information (%p):"
"%</u>%</b>\n\n", addr);
+ if (sr->smb2_cmd_code == 0) {
+ /* SMB1 request */
+ mdb_printf(
+ "first SMB COM: %u (%s)\n",
+ sr->first_smb_com,
+ smb_com[sr->first_smb_com].smb_com);
+ }
+
+ mdb_printf(
+ "current SMB COM: %u (%s)\n",
+ cur_cmd_code, cur_cmd_name);
+
+ mdb_printf(
+ "state: %u (%s)\n",
+ sr->sr_state, state);
+
+ 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);
+
+ mdb_printf(
+ "PID: %u\n",
+ sr->smb_pid);
+
+ if (sr->smb2_messageid != 0) {
+ mdb_printf(
+ "MID: 0x%llx\n\n",
+ sr->smb2_messageid);
+ } else {
+ mdb_printf(
+ "MID: %u\n\n",
+ sr->smb_mid);
+ }
+
+ mdb_printf(
+ "waiting time: %lld\n",
+ waiting);
+
mdb_printf(
- "first SMB COM: %u (%s)\n"
- "current SMB COM: %u (%s)\n"
- "state: %u (%s)\n"
- "TID(tree): %u (%p)\n"
- "UID(user): %u (%p)\n"
- "FID(file): %u (%p)\n"
- "PID: %u\n"
- "MID: %u\n\n"
- "waiting time: %lld\n"
"running time: %lld\n",
- sr->first_smb_com,
- smb_com[sr->first_smb_com].smb_com,
- sr->smb_com,
- smb_com[sr->smb_com].smb_com,
- sr->sr_state, state,
- sr->smb_tid, sr->tid_tree,
- sr->smb_uid, sr->uid_user,
- sr->smb_fid, sr->fid_ofile,
- sr->smb_pid,
- sr->smb_mid,
- waiting,
running);
+ mdb_printf(
+ "worker thread: %p\n",
+ sr->sr_worker);
smb_worker_findstack((uintptr_t)sr->sr_worker);
} else {
if (DCMD_HDRSPEC(flags))
@@ -952,13 +1005,14 @@ smb_dcmd_request(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
"STATE",
"COMMAND");
- mdb_printf(SMB_REQUEST_FORMAT,
+ mdb_printf(
+ SMB_REQUEST_FORMAT,
addr,
sr->sr_worker,
waiting,
running,
state,
- smb_com[sr->smb_com].smb_com);
+ cur_cmd_name);
}
}
return (DCMD_OK);
diff --git a/usr/src/cmd/smbsrv/smbd/server.xml b/usr/src/cmd/smbsrv/smbd/server.xml
index d8dc32c114..3364a193f3 100644
--- a/usr/src/cmd/smbsrv/smbd/server.xml
+++ b/usr/src/cmd/smbsrv/smbd/server.xml
@@ -22,7 +22,7 @@ information: Portions Copyright [yyyy] [name of copyright owner]
CDDL HEADER END
Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
-Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+Copyright 2015 Nexenta Systems, Inc. All rights reserved.
NOTE: This service manifest is not editable; its contents will
be overwritten by package or patch operations, including
@@ -216,8 +216,16 @@ file.
value='5.0' override='true'/>
<propval name='dfs_stdroot_num' type='integer'
value='0' override='true'/>
+ <propval name='print_enable' type='boolean'
+ value='false' override='true'/>
<propval name='traverse_mounts' type='boolean'
value='true' override='true'/>
+ <propval name='max_protocol' type='astring'
+ value='' override='true'/>
+ <propval name='initial_credits' type='integer'
+ value='20' override='true'/>
+ <propval name='maximum_credits' type='integer'
+ value='1000' override='true'/>
</property_group>
<!-- SMB service-specific shares exec configuration defaults -->
diff --git a/usr/src/cmd/smbsrv/smbd/smbd.h b/usr/src/cmd/smbsrv/smbd/smbd.h
index 436120a745..c389813f9d 100644
--- a/usr/src/cmd/smbsrv/smbd/smbd.h
+++ b/usr/src/cmd/smbsrv/smbd/smbd.h
@@ -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 2014 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SMBD_H
@@ -67,7 +67,7 @@ void smbd_load_printers(void);
int smbd_vss_get_count(const char *, uint32_t *);
void smbd_vss_get_snapshots(const char *, uint32_t, uint32_t *,
uint32_t *, char **);
-int smbd_vss_map_gmttoken(const char *, char *, char *);
+int smbd_vss_map_gmttoken(const char *, char *, time_t, char *);
typedef struct smbd {
const char *s_version; /* smbd version string */
diff --git a/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c b/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c
index 6de84cadbe..ce606b7b97 100644
--- a/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c
+++ b/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.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 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/list.h>
@@ -806,7 +806,7 @@ smbd_dop_vss_map_gmttoken(smbd_arg_t *arg)
}
if ((smbd_vss_map_gmttoken(request.gts_path, request.gts_gmttoken,
- snapname) != 0)) {
+ request.gts_toktime, snapname) != 0)) {
*snapname = '\0';
}
diff --git a/usr/src/cmd/smbsrv/smbd/smbd_main.c b/usr/src/cmd/smbsrv/smbd/smbd_main.c
index b1c9dbc770..21dc7a20d1 100644
--- a/usr/src/cmd/smbsrv/smbd/smbd_main.c
+++ b/usr/src/cmd/smbsrv/smbd/smbd_main.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 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -473,6 +473,11 @@ smbd_service_init(void)
return (-1);
}
+#ifndef FKSMBD
+ /* Upgrade SMF settings, if necessary. */
+ smb_config_upgrade();
+#endif
+
smb_codepage_init();
rc = smbd_cups_init();
diff --git a/usr/src/cmd/smbsrv/smbd/smbd_vss.c b/usr/src/cmd/smbsrv/smbd/smbd_vss.c
index 411037b8e6..be78fc1ccc 100644
--- a/usr/src/cmd/smbsrv/smbd/smbd_vss.c
+++ b/usr/src/cmd/smbsrv/smbd/smbd_vss.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <synch.h>
@@ -88,7 +89,7 @@ typedef struct smbd_vss_get_uint64_date {
} smbd_vss_get_uint64_date_t;
typedef struct smbd_vss_map_gmttoken {
- char *mg_gmttoken;
+ time_t mg_snaptime;
char *mg_snapname;
} smbd_vss_map_gmttoken_t;
@@ -214,10 +215,14 @@ smbd_vss_get_snapshots(const char *path, uint32_t count,
libzfs_fini(libhd);
}
+static const char
+smbd_vss_gmttoken_fmt[] = "@GMT-%Y.%m.%d-%H.%M.%S";
+
/*
* path - path of the dataset for the operation
* gmttoken - the @GMT token to be looked up
- * snapname - the snapshot name to be returned
+ * toktime - time_t used if gmttoken == NULL
+ * snapname - the snapshot name to be returned [MAXPATHLEN]
*
* Here we are going to get the snapshot name from the @GMT token
* The snapname returned by ZFS is : <dataset name>@<snapshot name>
@@ -225,7 +230,8 @@ smbd_vss_get_snapshots(const char *path, uint32_t count,
* the right place and then just return the snapshot name
*/
int
-smbd_vss_map_gmttoken(const char *path, char *gmttoken, char *snapname)
+smbd_vss_map_gmttoken(const char *path, char *gmttoken, time_t toktime,
+ char *snapname)
{
char dataset[MAXPATHLEN];
libzfs_handle_t *libhd;
@@ -233,8 +239,14 @@ smbd_vss_map_gmttoken(const char *path, char *gmttoken, char *snapname)
smbd_vss_map_gmttoken_t vss_map_gmttoken;
char *zsnap;
const char *lsnap;
+ struct tm tm;
+
+ if (gmttoken != NULL && *gmttoken == '@' &&
+ strptime(gmttoken, smbd_vss_gmttoken_fmt, &tm) != NULL) {
+ toktime = timegm(&tm);
+ }
- vss_map_gmttoken.mg_gmttoken = gmttoken;
+ vss_map_gmttoken.mg_snaptime = toktime;
vss_map_gmttoken.mg_snapname = snapname;
*snapname = '\0';
@@ -281,7 +293,7 @@ smbd_vss_time2gmttoken(time_t time, char *gmttoken)
(void) gmtime_r(&time, &t);
(void) strftime(gmttoken, SMB_VSS_GMT_SIZE,
- "@GMT-%Y.%m.%d-%H.%M.%S", &t);
+ smbd_vss_gmttoken_fmt, &t);
}
static int
@@ -349,12 +361,9 @@ smbd_vss_iterate_map_gmttoken(zfs_handle_t *zhp, void *data)
{
smbd_vss_map_gmttoken_t *vss_data = data;
time_t time;
- char gmttoken[SMB_VSS_GMT_SIZE];
time = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION);
- smbd_vss_time2gmttoken(time, gmttoken);
-
- if (strncmp(gmttoken, vss_data->mg_gmttoken, SMB_VSS_GMT_SIZE) == 0) {
+ if (time == vss_data->mg_snaptime) {
(void) strlcpy(vss_data->mg_snapname, zfs_get_name(zhp),
MAXPATHLEN);
diff --git a/usr/src/cmd/smbsrv/smbstat/smbstat.c b/usr/src/cmd/smbsrv/smbstat/smbstat.c
index 1cc91b7232..c8ea26d7f9 100644
--- a/usr/src/cmd/smbsrv/smbstat/smbstat.c
+++ b/usr/src/cmd/smbsrv/smbstat/smbstat.c
@@ -20,8 +20,8 @@
*/
/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -214,7 +214,8 @@ typedef struct smbstat_srv_info {
/*
* Latency & Throughput per request
*/
- smbstat_req_info_t si_reqs[SMB_COM_NUM];
+ smbstat_req_info_t si_reqs1[SMB_COM_NUM];
+ smbstat_req_info_t si_reqs2[SMB2__NCMDS];
} smbstat_srv_info_t;
static void smbstat_init(void);
@@ -252,6 +253,9 @@ static void smbstat_srv_process_utilization(smbstat_srv_snapshot_t *,
smbstat_srv_snapshot_t *);
static void smbstat_srv_process_requests(smbstat_srv_snapshot_t *,
smbstat_srv_snapshot_t *);
+static void smbstat_srv_process_one_req(smbstat_req_info_t *,
+ smb_kstat_req_t *, smb_kstat_req_t *, boolean_t);
+
static smbstat_srv_snapshot_t *smbstat_srv_current_snapshot(void);
static smbstat_srv_snapshot_t *smbstat_srv_previous_snapshot(void);
@@ -445,6 +449,7 @@ smbstat_kstat_print(void)
smbstat_print_throughput();
smbstat_print_utilization();
smbstat_print_requests();
+ (void) fflush(stdout);
}
/*
@@ -549,10 +554,9 @@ smbstat_print_requests(void)
if (!smbstat_opt_r)
return;
- prq = smbstat_srv_info.si_reqs;
-
(void) printf(SMBSRV_REQUESTS_BANNER, " ");
+ prq = smbstat_srv_info.si_reqs1;
for (i = 0; i < SMB_COM_NUM; i++) {
if (!smbstat_opt_a &&
strncmp(prq[i].ri_name, "Invalid", sizeof ("Invalid")) == 0)
@@ -570,6 +574,24 @@ smbstat_print_requests(void)
prq[i].ri_stddev);
}
}
+
+ prq = smbstat_srv_info.si_reqs2;
+ for (i = 0; i < SMB2__NCMDS; i++) {
+ if (!smbstat_opt_a && i == SMB2_INVALID_CMD)
+ continue;
+
+ if (!smbstat_opt_z || (prq[i].ri_pct != 0)) {
+ (void) printf(SMBSRV_REQUESTS_FORMAT,
+ prq[i].ri_name,
+ prq[i].ri_opcode,
+ smbstat_zero(prq[i].ri_pct),
+ smbstat_zero(prq[i].ri_rbs),
+ smbstat_zero(prq[i].ri_tbs),
+ smbstat_zero(prq[i].ri_rqs),
+ prq[i].ri_mean,
+ prq[i].ri_stddev);
+ }
+ }
}
/*
@@ -754,7 +776,7 @@ smbstat_wrk_process(void)
curr = smbstat_wrk_current_snapshot();
- if (curr->ws_maxthreads >= curr->ws_bnalloc)
+ if (curr->ws_bnalloc >= curr->ws_maxthreads)
smbstat_srv_info.si_sat = B_TRUE;
else
smbstat_srv_info.si_sat = B_FALSE;
@@ -881,34 +903,40 @@ smbstat_srv_process_throughput(
smbstat_srv_info.si_rqs /= smbstat_srv_info.si_etime;
smbstat_srv_info.si_rds = smbstat_sub_64(
- curr->ss_data.ks_reqs[SMB_COM_READ].kr_nreq,
- prev->ss_data.ks_reqs[SMB_COM_READ].kr_nreq);
+ curr->ss_data.ks_reqs1[SMB_COM_READ].kr_nreq,
+ prev->ss_data.ks_reqs1[SMB_COM_READ].kr_nreq);
+ smbstat_srv_info.si_rds += smbstat_sub_64(
+ curr->ss_data.ks_reqs1[SMB_COM_LOCK_AND_READ].kr_nreq,
+ prev->ss_data.ks_reqs1[SMB_COM_LOCK_AND_READ].kr_nreq);
smbstat_srv_info.si_rds += smbstat_sub_64(
- curr->ss_data.ks_reqs[SMB_COM_LOCK_AND_READ].kr_nreq,
- prev->ss_data.ks_reqs[SMB_COM_LOCK_AND_READ].kr_nreq);
+ curr->ss_data.ks_reqs1[SMB_COM_READ_RAW].kr_nreq,
+ prev->ss_data.ks_reqs1[SMB_COM_READ_RAW].kr_nreq);
smbstat_srv_info.si_rds += smbstat_sub_64(
- curr->ss_data.ks_reqs[SMB_COM_READ_RAW].kr_nreq,
- prev->ss_data.ks_reqs[SMB_COM_READ_RAW].kr_nreq);
+ curr->ss_data.ks_reqs1[SMB_COM_READ_ANDX].kr_nreq,
+ prev->ss_data.ks_reqs1[SMB_COM_READ_ANDX].kr_nreq);
smbstat_srv_info.si_rds += smbstat_sub_64(
- curr->ss_data.ks_reqs[SMB_COM_READ_ANDX].kr_nreq,
- prev->ss_data.ks_reqs[SMB_COM_READ_ANDX].kr_nreq);
+ curr->ss_data.ks_reqs2[SMB2_READ].kr_nreq,
+ prev->ss_data.ks_reqs2[SMB2_READ].kr_nreq);
smbstat_srv_info.si_rds /= smbstat_srv_info.si_etime;
smbstat_srv_info.si_wrs = smbstat_sub_64(
- curr->ss_data.ks_reqs[SMB_COM_WRITE].kr_nreq,
- prev->ss_data.ks_reqs[SMB_COM_WRITE].kr_nreq);
+ curr->ss_data.ks_reqs1[SMB_COM_WRITE].kr_nreq,
+ prev->ss_data.ks_reqs1[SMB_COM_WRITE].kr_nreq);
smbstat_srv_info.si_wrs += smbstat_sub_64(
- curr->ss_data.ks_reqs[SMB_COM_WRITE_AND_UNLOCK].kr_nreq,
- prev->ss_data.ks_reqs[SMB_COM_WRITE_AND_UNLOCK].kr_nreq);
+ curr->ss_data.ks_reqs1[SMB_COM_WRITE_AND_UNLOCK].kr_nreq,
+ prev->ss_data.ks_reqs1[SMB_COM_WRITE_AND_UNLOCK].kr_nreq);
smbstat_srv_info.si_wrs += smbstat_sub_64(
- curr->ss_data.ks_reqs[SMB_COM_WRITE_RAW].kr_nreq,
- prev->ss_data.ks_reqs[SMB_COM_WRITE_RAW].kr_nreq);
+ curr->ss_data.ks_reqs1[SMB_COM_WRITE_RAW].kr_nreq,
+ prev->ss_data.ks_reqs1[SMB_COM_WRITE_RAW].kr_nreq);
smbstat_srv_info.si_wrs += smbstat_sub_64(
- curr->ss_data.ks_reqs[SMB_COM_WRITE_AND_CLOSE].kr_nreq,
- prev->ss_data.ks_reqs[SMB_COM_WRITE_AND_CLOSE].kr_nreq);
+ curr->ss_data.ks_reqs1[SMB_COM_WRITE_AND_CLOSE].kr_nreq,
+ prev->ss_data.ks_reqs1[SMB_COM_WRITE_AND_CLOSE].kr_nreq);
smbstat_srv_info.si_wrs += smbstat_sub_64(
- curr->ss_data.ks_reqs[SMB_COM_WRITE_ANDX].kr_nreq,
- prev->ss_data.ks_reqs[SMB_COM_WRITE_ANDX].kr_nreq);
+ curr->ss_data.ks_reqs1[SMB_COM_WRITE_ANDX].kr_nreq,
+ prev->ss_data.ks_reqs1[SMB_COM_WRITE_ANDX].kr_nreq);
+ smbstat_srv_info.si_wrs += smbstat_sub_64(
+ curr->ss_data.ks_reqs2[SMB2_WRITE].kr_nreq,
+ prev->ss_data.ks_reqs2[SMB2_WRITE].kr_nreq);
smbstat_srv_info.si_wrs /= smbstat_srv_info.si_etime;
}
@@ -999,55 +1027,79 @@ smbstat_srv_process_requests(
smbstat_srv_snapshot_t *prev)
{
smbstat_req_info_t *info;
- double nrqs;
+ smb_kstat_req_t *curr_req;
+ smb_kstat_req_t *prev_req;
int i, idx;
-
- info = smbstat_srv_info.si_reqs;
+ boolean_t firstcall = (prev->ss_snaptime == 0);
for (i = 0; i < SMB_COM_NUM; i++) {
- idx = info[i].ri_opcode;
-
- nrqs = smbstat_sub_64(curr->ss_data.ks_reqs[idx].kr_nreq,
- prev->ss_data.ks_reqs[idx].kr_nreq);
-
- info[i].ri_rqs = nrqs / smbstat_srv_info.si_etime;
-
- info[i].ri_rbs = smbstat_sub_64(
- curr->ss_data.ks_reqs[idx].kr_rxb,
- prev->ss_data.ks_reqs[idx].kr_rxb) /
- smbstat_srv_info.si_etime;
-
- info[i].ri_tbs = smbstat_sub_64(
- curr->ss_data.ks_reqs[idx].kr_txb,
- prev->ss_data.ks_reqs[idx].kr_txb) /
- smbstat_srv_info.si_etime;
-
- info[i].ri_pct = nrqs * 100;
- if (smbstat_srv_info.si_total_nreqs > 0)
- info[i].ri_pct /= smbstat_srv_info.si_total_nreqs;
-
- if (prev->ss_snaptime == 0) {
- /* First time. Take the aggregate */
- info[i].ri_stddev =
- curr->ss_data.ks_reqs[idx].kr_a_stddev;
- info[i].ri_mean = curr->ss_data.ks_reqs[idx].kr_a_mean;
- } else {
- /* Take the differential */
- info[i].ri_stddev =
- curr->ss_data.ks_reqs[idx].kr_d_stddev;
- info[i].ri_mean = curr->ss_data.ks_reqs[idx].kr_d_mean;
- }
- if (nrqs > 0) {
- info[i].ri_stddev /= nrqs;
- info[i].ri_stddev = sqrt(info[i].ri_stddev);
- } else {
- info[i].ri_stddev = 0;
- }
- info[i].ri_stddev /= NANOSEC;
- info[i].ri_mean /= NANOSEC;
+ info = &smbstat_srv_info.si_reqs1[i];
+ idx = info[i].ri_opcode & 0xFF;
+ curr_req = &curr->ss_data.ks_reqs1[idx];
+ prev_req = &prev->ss_data.ks_reqs1[idx];
+ smbstat_srv_process_one_req(
+ info, curr_req, prev_req, firstcall);
+ }
+
+ for (i = 0; i < SMB2__NCMDS; i++) {
+ info = &smbstat_srv_info.si_reqs2[i];
+ curr_req = &curr->ss_data.ks_reqs2[i];
+ prev_req = &prev->ss_data.ks_reqs2[i];
+ smbstat_srv_process_one_req(
+ info, curr_req, prev_req, firstcall);
+ }
+}
+
+static void
+smbstat_srv_process_one_req(
+ smbstat_req_info_t *info,
+ smb_kstat_req_t *curr_req,
+ smb_kstat_req_t *prev_req,
+ boolean_t firstcall)
+{
+ double nrqs;
+
+ nrqs = smbstat_sub_64(curr_req->kr_nreq,
+ prev_req->kr_nreq);
+
+ info->ri_rqs = nrqs / smbstat_srv_info.si_etime;
+
+ info->ri_rbs = smbstat_sub_64(
+ curr_req->kr_rxb,
+ prev_req->kr_rxb) /
+ smbstat_srv_info.si_etime;
+
+ info->ri_tbs = smbstat_sub_64(
+ curr_req->kr_txb,
+ prev_req->kr_txb) /
+ smbstat_srv_info.si_etime;
+
+ info->ri_pct = nrqs * 100;
+ if (smbstat_srv_info.si_total_nreqs > 0)
+ info->ri_pct /= smbstat_srv_info.si_total_nreqs;
+
+ if (firstcall) {
+ /* First time. Take the aggregate */
+ info->ri_stddev =
+ curr_req->kr_a_stddev;
+ info->ri_mean = curr_req->kr_a_mean;
+ } else {
+ /* Take the differential */
+ info->ri_stddev =
+ curr_req->kr_d_stddev;
+ info->ri_mean = curr_req->kr_d_mean;
+ }
+ if (nrqs > 0) {
+ info->ri_stddev /= nrqs;
+ info->ri_stddev = sqrt(info->ri_stddev);
+ } else {
+ info->ri_stddev = 0;
}
+ info->ri_stddev /= NANOSEC;
+ info->ri_mean /= NANOSEC;
}
+
/*
* smbstat_srv_current_snapshot
*
@@ -1216,14 +1268,16 @@ smbstat_req_cmp_name(const void *obj1, const void *obj2)
static void
smbstat_req_order(void)
{
+ smbstat_srv_snapshot_t *ss;
smbstat_req_info_t *info;
smb_kstat_req_t *reqs;
int i;
smbstat_srv_snapshot();
- reqs = smbstat_srv_current_snapshot()->ss_data.ks_reqs;
- info = smbstat_srv_info.si_reqs;
+ ss = smbstat_srv_current_snapshot();
+ reqs = ss->ss_data.ks_reqs1;
+ info = smbstat_srv_info.si_reqs1;
for (i = 0; i < SMB_COM_NUM; i++) {
(void) strlcpy(info[i].ri_name, reqs[i].kr_name,
sizeof (reqs[i].kr_name));
@@ -1232,6 +1286,17 @@ smbstat_req_order(void)
if (smbstat_opt_n)
qsort(info, SMB_COM_NUM, sizeof (smbstat_req_info_t),
smbstat_req_cmp_name);
+
+ reqs = ss->ss_data.ks_reqs2;
+ info = smbstat_srv_info.si_reqs2;
+ for (i = 0; i < SMB2__NCMDS; i++) {
+ (void) strlcpy(info[i].ri_name, reqs[i].kr_name,
+ sizeof (reqs[i].kr_name));
+ info[i].ri_opcode = i;
+ }
+ if (smbstat_opt_n)
+ qsort(info, SMB2__NCMDS, sizeof (smbstat_req_info_t),
+ smbstat_req_cmp_name);
}
/*
diff --git a/usr/src/common/smbclnt/smb_status2winerr.c b/usr/src/common/smbclnt/smb_status2winerr.c
new file mode 100644
index 0000000000..8a2cd9d38c
--- /dev/null
+++ b/usr/src/common/smbclnt/smb_status2winerr.c
@@ -0,0 +1,744 @@
+/*
+ * 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 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Table mapping NT status codes to Win32 errors. See:
+ * http://support.microsoft.com/kb/113996
+ *
+ * The table below was generated from the above KB data
+ * using a simple awk program.
+ */
+
+#include <sys/errno.h>
+#include <smb/ntstatus.h>
+#include <smb/nterror.h>
+
+#include "smb_status2winerr.h"
+
+
+const struct status2winerr
+smb_status2winerr_map[] = {
+
+ /* As this was generated, don't really want cstyle noise. */
+ /* BEGIN CSTYLED */
+
+ /*
+ * WINDOWS NT STATUS CODE WIN32 ERROR CODE
+ */
+ NT_STATUS_ACCESS_DENIED, ERROR_ACCESS_DENIED,
+ NT_STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT, ERROR_ACCESS_DISABLED_BY_POLICY,
+ NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER, ERROR_ACCESS_DISABLED_BY_POLICY,
+ NT_STATUS_ACCESS_DISABLED_BY_POLICY_PATH, ERROR_ACCESS_DISABLED_BY_POLICY,
+ NT_STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER, ERROR_ACCESS_DISABLED_BY_POLICY,
+ NT_STATUS_ACCESS_VIOLATION, ERROR_NOACCESS,
+ NT_STATUS_ACCOUNT_DISABLED, ERROR_ACCOUNT_DISABLED,
+ NT_STATUS_ACCOUNT_EXPIRED, ERROR_ACCOUNT_EXPIRED,
+ NT_STATUS_ACCOUNT_LOCKED_OUT, ERROR_ACCOUNT_LOCKED_OUT,
+ NT_STATUS_ACCOUNT_RESTRICTION, ERROR_ACCOUNT_RESTRICTION,
+ NT_STATUS_ADAPTER_HARDWARE_ERROR, ERROR_ADAP_HDW_ERR,
+ NT_STATUS_ADDRESS_ALREADY_ASSOCIATED, ERROR_ADDRESS_ALREADY_ASSOCIATED,
+ NT_STATUS_ADDRESS_ALREADY_EXISTS, ERROR_DUP_NAME,
+ NT_STATUS_ADDRESS_CLOSED, ERROR_NETNAME_DELETED,
+ NT_STATUS_ADDRESS_NOT_ASSOCIATED, ERROR_ADDRESS_NOT_ASSOCIATED,
+ NT_STATUS_AGENTS_EXHAUSTED, ERROR_NO_MORE_ITEMS,
+ NT_STATUS_ALIAS_EXISTS, ERROR_ALIAS_EXISTS,
+ NT_STATUS_ALLOTTED_SPACE_EXCEEDED, ERROR_ALLOTTED_SPACE_EXCEEDED,
+ NT_STATUS_ALREADY_COMMITTED, ERROR_ACCESS_DENIED,
+ NT_STATUS_ALREADY_DISCONNECTED, ERROR_ACTIVE_CONNECTIONS,
+ /* NT_STATUS_AUDITING_DISABLED, ERROR_AUDITING_DISABLED, */
+ /* NT_STATUS_BAD_BINDINGS, SEC_E_BAD_BINDINGS, */
+ NT_STATUS_BAD_DESCRIPTOR_FORMAT, ERROR_BAD_DESCRIPTOR_FORMAT,
+ NT_STATUS_BAD_DEVICE_TYPE, ERROR_BAD_DEV_TYPE,
+ NT_STATUS_BAD_IMPERSONATION_LEVEL, ERROR_BAD_IMPERSONATION_LEVEL,
+ NT_STATUS_BAD_INHERITANCE_ACL, ERROR_BAD_INHERITANCE_ACL,
+ NT_STATUS_BAD_INITIAL_PC, ERROR_BAD_EXE_FORMAT,
+ NT_STATUS_BAD_INITIAL_STACK, ERROR_STACK_OVERFLOW,
+ NT_STATUS_BAD_LOGON_SESSION_STATE, ERROR_BAD_LOGON_SESSION_STATE,
+ NT_STATUS_BAD_MASTER_BOOT_RECORD, ERROR_INVALID_PARAMETER,
+ NT_STATUS_BAD_NETWORK_NAME, ERROR_BAD_NET_NAME,
+ NT_STATUS_BAD_NETWORK_PATH, ERROR_BAD_NETPATH,
+ NT_STATUS_BAD_REMOTE_ADAPTER, ERROR_BAD_REM_ADAP,
+ NT_STATUS_BAD_TOKEN_TYPE, ERROR_BAD_TOKEN_TYPE,
+ NT_STATUS_BAD_VALIDATION_CLASS, ERROR_BAD_VALIDATION_CLASS,
+ NT_STATUS_BAD_WORKING_SET_LIMIT, ERROR_INVALID_PARAMETER,
+ NT_STATUS_BEGINNING_OF_MEDIA, ERROR_BEGINNING_OF_MEDIA,
+ NT_STATUS_BUFFER_OVERFLOW, ERROR_MORE_DATA,
+ NT_STATUS_BUFFER_TOO_SMALL, ERROR_INSUFFICIENT_BUFFER,
+ NT_STATUS_BUS_RESET, ERROR_BUS_RESET,
+ NT_STATUS_CANCELLED, ERROR_OPERATION_ABORTED,
+ NT_STATUS_CANNOT_DELETE, ERROR_ACCESS_DENIED,
+ NT_STATUS_CANNOT_IMPERSONATE, ERROR_CANNOT_IMPERSONATE,
+ NT_STATUS_CANNOT_MAKE, ERROR_CANNOT_MAKE,
+ NT_STATUS_CANT_ACCESS_DOMAIN_INFO, ERROR_CANT_ACCESS_DOMAIN_INFO,
+ NT_STATUS_CANT_DISABLE_MANDATORY, ERROR_CANT_DISABLE_MANDATORY,
+ NT_STATUS_CANT_OPEN_ANONYMOUS, ERROR_CANT_OPEN_ANONYMOUS,
+ NT_STATUS_CHILD_MUST_BE_VOLATILE, ERROR_CHILD_MUST_BE_VOLATILE,
+ /* NT_STATUS_CLEANER_CARTRIDGE_INSTALLED, ERROR_CLEANER_CARTRIDGE_INSTALLED, */
+ /* NT_STATUS_CLUSTER_... */
+ NT_STATUS_COMMITMENT_LIMIT, ERROR_COMMITMENT_LIMIT,
+ NT_STATUS_CONFLICTING_ADDRESSES, ERROR_INVALID_ADDRESS,
+ NT_STATUS_CONNECTION_ABORTED, ERROR_CONNECTION_ABORTED,
+ NT_STATUS_CONNECTION_ACTIVE, ERROR_CONNECTION_ACTIVE,
+ NT_STATUS_CONNECTION_COUNT_LIMIT, ERROR_CONNECTION_COUNT_LIMIT,
+ NT_STATUS_CONNECTION_DISCONNECTED, ERROR_NETNAME_DELETED,
+ NT_STATUS_CONNECTION_INVALID, ERROR_CONNECTION_INVALID,
+ NT_STATUS_CONNECTION_IN_USE, ERROR_DEVICE_IN_USE,
+ NT_STATUS_CONNECTION_REFUSED, ERROR_CONNECTION_REFUSED,
+ NT_STATUS_CONNECTION_RESET, ERROR_NETNAME_DELETED,
+ /* NT_STATUS_COPY_PROTECTION_FAILURE, STG_E_STATUS_COPY_PROTECTION_FAILURE, */
+ NT_STATUS_CRC_ERROR, ERROR_CRC,
+ /* NT_STATUS_CRYPTO_SYSTEM_INVALID, SEC_E_CRYPTO_SYSTEM_INVALID, */
+ /* NT_STATUS_CSS_... */
+ NT_STATUS_CTL_FILE_NOT_SUPPORTED, ERROR_NOT_SUPPORTED,
+ /* NT_STATUS_CTX_BAD_VIDEO_MODE, ERROR_CTX_BAD_VIDEO_MODE, */
+ /* NT_STATUS_CTX_... */
+ NT_STATUS_CURRENT_DOMAIN_NOT_ALLOWED, ERROR_CURRENT_DOMAIN_NOT_ALLOWED,
+ NT_STATUS_DATATYPE_MISALIGNMENT, ERROR_NOACCESS,
+ NT_STATUS_DATATYPE_MISALIGNMENT_ERROR, ERROR_NOACCESS,
+ NT_STATUS_DATA_ERROR, ERROR_CRC,
+ NT_STATUS_DATA_LATE_ERROR, ERROR_IO_DEVICE,
+ NT_STATUS_DATA_OVERRUN, ERROR_IO_DEVICE,
+ NT_STATUS_DECRYPTION_FAILED, ERROR_ACCESS_DENIED,
+ NT_STATUS_DELETE_PENDING, ERROR_ACCESS_DENIED,
+ NT_STATUS_DESTINATION_ELEMENT_FULL, ERROR_DESTINATION_ELEMENT_FULL,
+ NT_STATUS_DEVICE_BUSY, ERROR_BUSY,
+ NT_STATUS_DEVICE_CONFIGURATION_ERROR, ERROR_INVALID_PARAMETER,
+ NT_STATUS_DEVICE_DATA_ERROR, ERROR_CRC,
+ NT_STATUS_DEVICE_DOES_NOT_EXIST, ERROR_DEV_NOT_EXIST,
+ NT_STATUS_DEVICE_DOOR_OPEN, ERROR_DEVICE_DOOR_OPEN,
+ NT_STATUS_DEVICE_NOT_CONNECTED, ERROR_DEVICE_NOT_CONNECTED,
+ NT_STATUS_DEVICE_NOT_PARTITIONED, ERROR_DEVICE_NOT_PARTITIONED,
+ NT_STATUS_DEVICE_NOT_READY, ERROR_NOT_READY,
+ NT_STATUS_DEVICE_OFF_LINE, ERROR_NOT_READY,
+ NT_STATUS_DEVICE_PAPER_EMPTY, ERROR_OUT_OF_PAPER,
+ NT_STATUS_DEVICE_POWERED_OFF, ERROR_NOT_READY,
+ NT_STATUS_DEVICE_POWER_FAILURE, ERROR_NOT_READY,
+ NT_STATUS_DEVICE_PROTOCOL_ERROR, ERROR_IO_DEVICE,
+ NT_STATUS_DEVICE_REMOVED, ERROR_DEVICE_REMOVED,
+ NT_STATUS_DEVICE_REQUIRES_CLEANING, ERROR_DEVICE_REQUIRES_CLEANING,
+ NT_STATUS_DFS_EXIT_PATH_FOUND, ERROR_PATH_NOT_FOUND,
+ NT_STATUS_DFS_UNAVAILABLE, ERROR_CONNECTION_UNAVAIL,
+ NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT, ERROR_BAD_PATHNAME,
+ NT_STATUS_DIRECTORY_NOT_EMPTY, ERROR_DIR_NOT_EMPTY,
+ NT_STATUS_DIRECTORY_SERVICE_REQUIRED, ERROR_DS_DS_REQUIRED,
+ /* NT_STATUS_DISCONNECTED, ERROR_DEV_NOT_EXIST, */
+ NT_STATUS_DISK_CORRUPT_ERROR, ERROR_DISK_CORRUPT,
+ NT_STATUS_DISK_FULL, ERROR_DISK_FULL,
+ NT_STATUS_DISK_OPERATION_FAILED, ERROR_DISK_OPERATION_FAILED,
+ NT_STATUS_DISK_RECALIBRATE_FAILED, ERROR_DISK_RECALIBRATE_FAILED,
+ NT_STATUS_DISK_RESET_FAILED, ERROR_DISK_RESET_FAILED,
+ NT_STATUS_DLL_INIT_FAILED, ERROR_DLL_INIT_FAILED,
+ NT_STATUS_DLL_NOT_FOUND, ERROR_MOD_NOT_FOUND,
+ NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND, ERROR_DOMAIN_CONTROLLER_NOT_FOUND,
+ NT_STATUS_DOMAIN_EXISTS, ERROR_DOMAIN_EXISTS,
+ NT_STATUS_DOMAIN_LIMIT_EXCEEDED, ERROR_DOMAIN_LIMIT_EXCEEDED,
+ NT_STATUS_DOMAIN_TRUST_INCONSISTENT, ERROR_DOMAIN_TRUST_INCONSISTENT,
+ NT_STATUS_DOWNGRADE_DETECTED, ERROR_DOWNGRADE_DETECTED,
+ NT_STATUS_DRIVER_BLOCKED, ERROR_DRIVER_BLOCKED,
+ NT_STATUS_DRIVER_BLOCKED_CRITICAL, ERROR_DRIVER_BLOCKED,
+ NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND, ERROR_PROC_NOT_FOUND,
+ NT_STATUS_DRIVER_INTERNAL_ERROR, ERROR_IO_DEVICE,
+ NT_STATUS_DRIVER_ORDINAL_NOT_FOUND, ERROR_INVALID_ORDINAL,
+ NT_STATUS_DRIVER_UNABLE_TO_LOAD, ERROR_BAD_DRIVER,
+ NT_STATUS_DS_ADMIN_LIMIT_EXCEEDED, ERROR_DS_ADMIN_LIMIT_EXCEEDED,
+ NT_STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER, ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER,
+ NT_STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS, ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS,
+ NT_STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED, ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED,
+ NT_STATUS_DS_BUSY, ERROR_DS_BUSY,
+ NT_STATUS_DS_CANT_MOD_OBJ_CLASS, ERROR_DS_CANT_MOD_OBJ_CLASS,
+ NT_STATUS_DS_CANT_MOD_PRIMARYGROUPID, ERROR_DS_CANT_MOD_PRIMARYGROUPID,
+ NT_STATUS_DS_CANT_ON_NON_LEAF, ERROR_DS_CANT_ON_NON_LEAF,
+ NT_STATUS_DS_CANT_ON_RDN, ERROR_DS_CANT_ON_RDN,
+ NT_STATUS_DS_CANT_START, ERROR_DS_CANT_START,
+ NT_STATUS_DS_CROSS_DOM_MOVE_FAILED, ERROR_DS_CROSS_DOM_MOVE_ERROR,
+ NT_STATUS_DS_GC_NOT_AVAILABLE, ERROR_DS_GC_NOT_AVAILABLE,
+ NT_STATUS_DS_GC_REQUIRED, ERROR_DS_GC_REQUIRED,
+ NT_STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER, ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER,
+ NT_STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER, ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER,
+ NT_STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER, ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER,
+ NT_STATUS_DS_HAVE_PRIMARY_MEMBERS, ERROR_DS_HAVE_PRIMARY_MEMBERS,
+ NT_STATUS_DS_INCORRECT_ROLE_OWNER, ERROR_DS_INCORRECT_ROLE_OWNER,
+ NT_STATUS_DS_INIT_FAILURE, ERROR_DS_INIT_FAILURE,
+ NT_STATUS_DS_INIT_FAILURE_CONSOLE, ERROR_DS_INIT_FAILURE_CONSOLE,
+ NT_STATUS_DS_INVALID_ATTRIBUTE_SYNTAX, ERROR_DS_INVALID_ATTRIBUTE_SYNTAX,
+ NT_STATUS_DS_INVALID_GROUP_TYPE, ERROR_DS_INVALID_GROUP_TYPE,
+ NT_STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER, ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER,
+ NT_STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY, ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY,
+ NT_STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED, ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED,
+ NT_STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY, ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY,
+ NT_STATUS_DS_NO_ATTRIBUTE_OR_VALUE, ERROR_DS_NO_ATTRIBUTE_OR_VALUE,
+ NT_STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS, ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS,
+ NT_STATUS_DS_NO_MORE_RIDS, ERROR_DS_NO_MORE_RIDS,
+ NT_STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN, ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN,
+ NT_STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN, ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN,
+ NT_STATUS_DS_NO_RIDS_ALLOCATED, ERROR_DS_NO_RIDS_ALLOCATED,
+ NT_STATUS_DS_OBJ_CLASS_VIOLATION, ERROR_DS_OBJ_CLASS_VIOLATION,
+ NT_STATUS_DS_RIDMGR_INIT_ERROR, ERROR_DS_RIDMGR_INIT_ERROR,
+ NT_STATUS_DS_SAM_INIT_FAILURE, ERROR_DS_SAM_INIT_FAILURE,
+ NT_STATUS_DS_SAM_INIT_FAILURE_CONSOLE, ERROR_DS_SAM_INIT_FAILURE_CONSOLE,
+ NT_STATUS_DS_SENSITIVE_GROUP_VIOLATION, ERROR_DS_SENSITIVE_GROUP_VIOLATION,
+ NT_STATUS_DS_SHUTTING_DOWN, ERROR_DS_SHUTTING_DOWN,
+ NT_STATUS_DS_UNAVAILABLE, ERROR_DS_UNAVAILABLE,
+ NT_STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER, ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER,
+ NT_STATUS_DUPLICATE_NAME, ERROR_DUP_NAME,
+ /* NT_STATUS_DUPLICATE_OBJECTID, STATUS_DUPLICATE_OBJECTID, */
+ NT_STATUS_EAS_NOT_SUPPORTED, ERROR_EAS_NOT_SUPPORTED,
+ NT_STATUS_EA_CORRUPT_ERROR, ERROR_FILE_CORRUPT,
+ NT_STATUS_EA_LIST_INCONSISTENT, ERROR_EA_LIST_INCONSISTENT,
+ NT_STATUS_EA_TOO_LARGE, ERROR_EA_LIST_INCONSISTENT,
+ NT_STATUS_EFS_ALG_BLOB_TOO_BIG, ERROR_EFS_ALG_BLOB_TOO_BIG,
+ NT_STATUS_ENCRYPTION_FAILED, ERROR_ACCESS_DENIED,
+ /* NT_STATUS_ENDPOINT_CLOSED, ERROR_DEV_NOT_EXIST, */
+ NT_STATUS_END_OF_FILE, ERROR_HANDLE_EOF,
+ NT_STATUS_END_OF_MEDIA, ERROR_END_OF_MEDIA,
+ NT_STATUS_ENTRYPOINT_NOT_FOUND, ERROR_PROC_NOT_FOUND,
+ NT_STATUS_EOM_OVERFLOW, ERROR_EOM_OVERFLOW,
+ NT_STATUS_EVENTLOG_CANT_START, ERROR_EVENTLOG_CANT_START,
+ NT_STATUS_EVENTLOG_FILE_CHANGED, ERROR_EVENTLOG_FILE_CHANGED,
+ NT_STATUS_EVENTLOG_FILE_CORRUPT, ERROR_EVENTLOG_FILE_CORRUPT,
+ NT_STATUS_FAIL_CHECK, ERROR_INVALID_PARAMETER,
+ NT_STATUS_FILEMARK_DETECTED, ERROR_FILEMARK_DETECTED,
+ NT_STATUS_FILES_OPEN, ERROR_OPEN_FILES,
+ NT_STATUS_FILE_CLOSED, ERROR_INVALID_HANDLE,
+ NT_STATUS_FILE_CORRUPT_ERROR, ERROR_FILE_CORRUPT,
+ NT_STATUS_FILE_DELETED, ERROR_ACCESS_DENIED,
+ NT_STATUS_FILE_ENCRYPTED, ERROR_FILE_ENCRYPTED,
+ NT_STATUS_FILE_FORCED_CLOSED, ERROR_HANDLE_EOF,
+ NT_STATUS_FILE_INVALID, ERROR_FILE_INVALID,
+ NT_STATUS_FILE_IS_A_DIRECTORY, ERROR_ACCESS_DENIED,
+ NT_STATUS_FILE_IS_OFFLINE, ERROR_FILE_OFFLINE,
+ NT_STATUS_FILE_LOCK_CONFLICT, ERROR_LOCK_VIOLATION,
+ NT_STATUS_FILE_NOT_ENCRYPTED, ERROR_FILE_NOT_ENCRYPTED,
+ NT_STATUS_FILE_RENAMED, ERROR_ACCESS_DENIED,
+ NT_STATUS_FLOPPY_BAD_REGISTERS, ERROR_FLOPPY_BAD_REGISTERS,
+ NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND, ERROR_FLOPPY_ID_MARK_NOT_FOUND,
+ NT_STATUS_FLOPPY_UNKNOWN_ERROR, ERROR_FLOPPY_UNKNOWN_ERROR,
+ NT_STATUS_FLOPPY_WRONG_CYLINDER, ERROR_FLOPPY_WRONG_CYLINDER,
+ NT_STATUS_FREE_VM_NOT_AT_BASE, ERROR_INVALID_ADDRESS,
+ NT_STATUS_FT_MISSING_MEMBER, ERROR_IO_DEVICE,
+ NT_STATUS_FT_ORPHANING, ERROR_IO_DEVICE,
+ NT_STATUS_FULLSCREEN_MODE, ERROR_FULLSCREEN_MODE,
+ NT_STATUS_GENERIC_NOT_MAPPED, ERROR_GENERIC_NOT_MAPPED,
+ NT_STATUS_GRACEFUL_DISCONNECT, ERROR_GRACEFUL_DISCONNECT,
+ NT_STATUS_GROUP_EXISTS, ERROR_GROUP_EXISTS,
+ NT_STATUS_GUIDS_EXHAUSTED, ERROR_NO_MORE_ITEMS,
+ NT_STATUS_HANDLE_NOT_CLOSABLE, ERROR_INVALID_HANDLE,
+ NT_STATUS_HOST_DOWN, ERROR_HOST_DOWN,
+ NT_STATUS_HOST_UNREACHABLE, ERROR_HOST_UNREACHABLE,
+ NT_STATUS_ILLEGAL_CHARACTER, ERROR_NO_UNICODE_TRANSLATION,
+ NT_STATUS_ILLEGAL_ELEMENT_ADDRESS, ERROR_ILLEGAL_ELEMENT_ADDRESS,
+ NT_STATUS_ILLEGAL_FUNCTION, ERROR_INVALID_FUNCTION,
+ NT_STATUS_ILL_FORMED_PASSWORD, ERROR_ILL_FORMED_PASSWORD,
+ NT_STATUS_IMAGE_ALREADY_LOADED, ERROR_SERVICE_ALREADY_RUNNING,
+ NT_STATUS_IMAGE_CHECKSUM_MISMATCH, ERROR_BAD_EXE_FORMAT,
+ NT_STATUS_IMAGE_MP_UP_MISMATCH, ERROR_BAD_EXE_FORMAT,
+ NT_STATUS_INCOMPATIBLE_FILE_MAP, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INFO_LENGTH_MISMATCH, ERROR_BAD_LENGTH,
+ NT_STATUS_INSTANCE_NOT_AVAILABLE, ERROR_PIPE_BUSY,
+ NT_STATUS_INSUFFICIENT_RESOURCES, ERROR_NO_SYSTEM_RESOURCES,
+ NT_STATUS_INSUFF_SERVER_RESOURCES, ERROR_NOT_ENOUGH_SERVER_MEMORY,
+ NT_STATUS_INTEGER_OVERFLOW, ERROR_ARITHMETIC_OVERFLOW,
+ NT_STATUS_INTERNAL_DB_CORRUPTION, ERROR_INTERNAL_DB_CORRUPTION,
+ NT_STATUS_INTERNAL_DB_ERROR, ERROR_INTERNAL_DB_ERROR,
+ NT_STATUS_INTERNAL_ERROR, ERROR_INTERNAL_ERROR,
+ NT_STATUS_INVALID_ACCOUNT_NAME, ERROR_INVALID_ACCOUNT_NAME,
+ NT_STATUS_INVALID_ACL, ERROR_INVALID_ACL,
+ NT_STATUS_INVALID_ADDRESS, ERROR_UNEXP_NET_ERR,
+ NT_STATUS_INVALID_ADDRESS_COMPONENT, ERROR_INVALID_NETNAME,
+ NT_STATUS_INVALID_ADDRESS_WILDCARD, ERROR_INVALID_NETNAME,
+ NT_STATUS_INVALID_BLOCK_LENGTH, ERROR_INVALID_BLOCK_LENGTH,
+ NT_STATUS_INVALID_BUFFER_SIZE, ERROR_INVALID_USER_BUFFER,
+ NT_STATUS_INVALID_CID, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_COMPUTER_NAME, ERROR_INVALID_COMPUTERNAME,
+ NT_STATUS_INVALID_CONNECTION, ERROR_UNEXP_NET_ERR,
+ NT_STATUS_INVALID_DEVICE_REQUEST, ERROR_INVALID_FUNCTION,
+ NT_STATUS_INVALID_DEVICE_STATE, ERROR_BAD_COMMAND,
+ NT_STATUS_INVALID_DOMAIN_ROLE, ERROR_INVALID_DOMAIN_ROLE,
+ NT_STATUS_INVALID_DOMAIN_STATE, ERROR_INVALID_DOMAIN_STATE,
+ NT_STATUS_INVALID_EA_FLAG, ERROR_EA_LIST_INCONSISTENT,
+ NT_STATUS_INVALID_EA_NAME, ERROR_INVALID_EA_NAME,
+ NT_STATUS_INVALID_FILE_FOR_SECTION, ERROR_BAD_EXE_FORMAT,
+ NT_STATUS_INVALID_GROUP_ATTRIBUTES, ERROR_INVALID_GROUP_ATTRIBUTES,
+ NT_STATUS_INVALID_HANDLE, ERROR_INVALID_HANDLE,
+ NT_STATUS_INVALID_ID_AUTHORITY, ERROR_INVALID_ID_AUTHORITY,
+ NT_STATUS_INVALID_IMAGE_FORMAT, ERROR_BAD_EXE_FORMAT,
+ NT_STATUS_INVALID_IMAGE_LE_FORMAT, ERROR_BAD_EXE_FORMAT,
+ NT_STATUS_INVALID_IMAGE_NE_FORMAT, ERROR_BAD_EXE_FORMAT,
+ NT_STATUS_INVALID_IMAGE_NOT_MZ, ERROR_BAD_EXE_FORMAT,
+ NT_STATUS_INVALID_IMAGE_PROTECT, ERROR_BAD_EXE_FORMAT,
+ NT_STATUS_INVALID_IMAGE_WIN_16, ERROR_BAD_EXE_FORMAT,
+ NT_STATUS_INVALID_IMAGE_WIN_32, ERROR_BAD_EXE_FORMAT,
+ NT_STATUS_INVALID_IMAGE_WIN_64, ERROR_BAD_EXE_FORMAT,
+ NT_STATUS_INVALID_IMPORT_OF_NON_DLL, ERROR_INVALID_IMPORT_OF_NON_DLL,
+ NT_STATUS_INVALID_INFO_CLASS, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_LEVEL, ERROR_INVALID_LEVEL,
+ NT_STATUS_INVALID_LOCK_SEQUENCE, ERROR_ACCESS_DENIED,
+ NT_STATUS_INVALID_LOGON_HOURS, ERROR_INVALID_LOGON_HOURS,
+ NT_STATUS_INVALID_LOGON_TYPE, ERROR_INVALID_LOGON_TYPE,
+ NT_STATUS_INVALID_MEMBER, ERROR_INVALID_MEMBER,
+ NT_STATUS_INVALID_NETWORK_RESPONSE, ERROR_BAD_NET_RESP,
+ NT_STATUS_INVALID_OPLOCK_PROTOCOL, ERROR_INVALID_OPLOCK_PROTOCOL,
+ NT_STATUS_INVALID_OWNER, ERROR_INVALID_OWNER,
+ NT_STATUS_INVALID_PAGE_PROTECTION, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_1, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_2, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_3, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_4, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_5, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_6, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_7, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_8, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_9, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_10, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_11, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_12, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PARAMETER_MIX, ERROR_INVALID_PARAMETER,
+ NT_STATUS_INVALID_PIPE_STATE, ERROR_BAD_PIPE,
+ NT_STATUS_INVALID_PORT_HANDLE, ERROR_INVALID_HANDLE,
+ NT_STATUS_INVALID_PRIMARY_GROUP, ERROR_INVALID_PRIMARY_GROUP,
+ NT_STATUS_INVALID_READ_MODE, ERROR_BAD_PIPE,
+ NT_STATUS_INVALID_SECURITY_DESCR, ERROR_INVALID_SECURITY_DESCR,
+ NT_STATUS_INVALID_SERVER_STATE, ERROR_INVALID_SERVER_STATE,
+ NT_STATUS_INVALID_SID, ERROR_INVALID_SID,
+ NT_STATUS_INVALID_SUB_AUTHORITY, ERROR_INVALID_SUB_AUTHORITY,
+ NT_STATUS_INVALID_SYSTEM_SERVICE, ERROR_INVALID_FUNCTION,
+ NT_STATUS_INVALID_USER_BUFFER, ERROR_INVALID_USER_BUFFER,
+ NT_STATUS_INVALID_VIEW_SIZE, ERROR_ACCESS_DENIED,
+ NT_STATUS_INVALID_VOLUME_LABEL, ERROR_LABEL_TOO_LONG,
+ NT_STATUS_INVALID_WORKSTATION, ERROR_INVALID_WORKSTATION,
+ NT_STATUS_IN_PAGE_ERROR, ERROR_SWAPERROR,
+ NT_STATUS_IO_DEVICE_ERROR, ERROR_IO_DEVICE,
+ NT_STATUS_IO_REPARSE_DATA_INVALID, ERROR_INVALID_REPARSE_DATA,
+ NT_STATUS_IO_REPARSE_TAG_INVALID, ERROR_REPARSE_TAG_INVALID,
+ NT_STATUS_IO_REPARSE_TAG_MISMATCH, ERROR_REPARSE_TAG_MISMATCH,
+ NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED, ERROR_CANT_ACCESS_FILE,
+ NT_STATUS_IO_TIMEOUT, ERROR_SEM_TIMEOUT,
+ NT_STATUS_ISSUING_CA_UNTRUSTED, SEC_E_ISSUING_CA_UNTRUSTED,
+ NT_STATUS_JOURNAL_DELETE_IN_PROGRESS, ERROR_JOURNAL_DELETE_IN_PROGRESS,
+ NT_STATUS_JOURNAL_ENTRY_DELETED, ERROR_JOURNAL_ENTRY_DELETED,
+ NT_STATUS_JOURNAL_NOT_ACTIVE, ERROR_JOURNAL_NOT_ACTIVE,
+ /* NT_STATUS_KDC_INVALID_REQUEST, SEC_E_KDC_INVALID_REQUEST, */
+ /* NT_STATUS_KDC_UNABLE_TO_REFER, SEC_E_KDC_UNABLE_TO_REFER, */
+ /* NT_STATUS_KDC_UNKNOWN_ETYPE, SEC_E_KDC_UNKNOWN_ETYPE, */
+ NT_STATUS_KEY_DELETED, ERROR_KEY_DELETED,
+ NT_STATUS_KEY_HAS_CHILDREN, ERROR_KEY_HAS_CHILDREN,
+ NT_STATUS_LAST_ADMIN, ERROR_LAST_ADMIN,
+ NT_STATUS_LICENSE_QUOTA_EXCEEDED, ERROR_LICENSE_QUOTA_EXCEEDED,
+ /* NT_STATUS_LICENSE_VIOLATION, ERROR_CTX_LICENSE_NOT_AVAILABLE, */
+ NT_STATUS_LINK_FAILED, ERROR_UNEXP_NET_ERR,
+ NT_STATUS_LINK_TIMEOUT, ERROR_UNEXP_NET_ERR,
+ NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED, ERROR_LM_CROSS_ENCRYPTION_REQUIRED,
+ NT_STATUS_LOCAL_DISCONNECT, ERROR_NETNAME_DELETED,
+ NT_STATUS_LOCAL_USER_SESSION_KEY, ERROR_LOCAL_USER_SESSION_KEY,
+ NT_STATUS_LOCK_NOT_GRANTED, ERROR_LOCK_VIOLATION,
+ NT_STATUS_LOGIN_TIME_RESTRICTION, ERROR_LOGIN_TIME_RESTRICTION,
+ NT_STATUS_LOGIN_WKSTA_RESTRICTION, ERROR_LOGIN_WKSTA_RESTRICTION,
+ NT_STATUS_LOGON_FAILURE, ERROR_LOGON_FAILURE,
+ NT_STATUS_LOGON_NOT_GRANTED, ERROR_LOGON_NOT_GRANTED,
+ NT_STATUS_LOGON_SESSION_COLLISION, ERROR_LOGON_SESSION_COLLISION,
+ NT_STATUS_LOGON_SESSION_EXISTS, ERROR_LOGON_SESSION_EXISTS,
+ NT_STATUS_LOGON_TYPE_NOT_GRANTED, ERROR_LOGON_TYPE_NOT_GRANTED,
+ NT_STATUS_LOG_FILE_FULL, ERROR_LOG_FILE_FULL,
+ NT_STATUS_LPC_REPLY_LOST, ERROR_CONNECTION_ABORTED,
+ NT_STATUS_LPC_REPLY_LOST, ERROR_INTERNAL_ERROR,
+ NT_STATUS_LUIDS_EXHAUSTED, ERROR_LUIDS_EXHAUSTED,
+ NT_STATUS_MAGAZINE_NOT_PRESENT, ERROR_MAGAZINE_NOT_PRESENT,
+ NT_STATUS_MAPPED_ALIGNMENT, ERROR_MAPPED_ALIGNMENT,
+ NT_STATUS_MAPPED_FILE_SIZE_ZERO, ERROR_FILE_INVALID,
+ /* NT_STATUS_MAX_REFERRALS_EXCEEDED, SEC_E_MAX_REFERRALS_EXCEEDED, */
+ NT_STATUS_MEDIA_CHANGED, ERROR_MEDIA_CHANGED,
+ NT_STATUS_MEDIA_WRITE_PROTECTED, ERROR_WRITE_PROTECT,
+ NT_STATUS_MEMBERS_PRIMARY_GROUP, ERROR_MEMBERS_PRIMARY_GROUP,
+ NT_STATUS_MEMBER_IN_ALIAS, ERROR_MEMBER_IN_ALIAS,
+ NT_STATUS_MEMBER_IN_GROUP, ERROR_MEMBER_IN_GROUP,
+ NT_STATUS_MEMBER_NOT_IN_ALIAS, ERROR_MEMBER_NOT_IN_ALIAS,
+ NT_STATUS_MEMBER_NOT_IN_GROUP, ERROR_MEMBER_NOT_IN_GROUP,
+ NT_STATUS_MEMORY_NOT_ALLOCATED, ERROR_INVALID_ADDRESS,
+ NT_STATUS_MESSAGE_NOT_FOUND, ERROR_MR_MID_NOT_FOUND,
+ NT_STATUS_MFT_TOO_FRAGMENTED, ERROR_DISK_TOO_FRAGMENTED,
+ NT_STATUS_MORE_ENTRIES, ERROR_MORE_DATA,
+ NT_STATUS_MORE_PROCESSING_REQUIRED, ERROR_MORE_DATA,
+ /* NT_STATUS_MUST_BE_KDC, SEC_E_MUST_BE_KDC, */
+ NT_STATUS_MUTANT_NOT_OWNED, ERROR_NOT_OWNER,
+ NT_STATUS_MUTUAL_AUTHENTICATION_FAILED, ERROR_MUTUAL_AUTH_FAILED,
+ NT_STATUS_NAME_TOO_LONG, ERROR_FILENAME_EXCED_RANGE,
+ NT_STATUS_NETLOGON_NOT_STARTED, ERROR_NETLOGON_NOT_STARTED,
+ NT_STATUS_NETWORK_ACCESS_DENIED, ERROR_NETWORK_ACCESS_DENIED,
+ NT_STATUS_NETWORK_BUSY, ERROR_NETWORK_BUSY,
+ NT_STATUS_NETWORK_CREDENTIAL_CONFLICT, ERROR_SESSION_CREDENTIAL_CONFLICT,
+ NT_STATUS_NETWORK_NAME_DELETED, ERROR_NETNAME_DELETED,
+ NT_STATUS_NETWORK_SESSION_EXPIRED, ERROR_NO_USER_SESSION_KEY,
+ NT_STATUS_NETWORK_UNREACHABLE, ERROR_NETWORK_UNREACHABLE,
+ NT_STATUS_NET_WRITE_FAULT, ERROR_NET_WRITE_FAULT,
+ NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT,
+ NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT, ERROR_NOLOGON_SERVER_TRUST_ACCOUNT,
+ NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT,
+ NT_STATUS_NONEXISTENT_EA_ENTRY, ERROR_FILE_CORRUPT,
+ /* NT_STATUS_NONEXISTENT_NET_NAME, ERROR_DEV_NOT_EXIST, */
+ NT_STATUS_NONEXISTENT_SECTOR, ERROR_SECTOR_NOT_FOUND,
+ NT_STATUS_NONE_MAPPED, ERROR_NONE_MAPPED,
+ NT_STATUS_NOTIFY_ENUM_DIR, ERROR_NOTIFY_ENUM_DIR,
+ NT_STATUS_NOT_ALL_ASSIGNED, ERROR_NOT_ALL_ASSIGNED,
+ NT_STATUS_NOT_A_DIRECTORY, ERROR_DIRECTORY,
+ NT_STATUS_NOT_A_REPARSE_POINT, ERROR_NOT_A_REPARSE_POINT,
+ NT_STATUS_NOT_CLIENT_SESSION, ERROR_NOT_SUPPORTED,
+ NT_STATUS_NOT_COMMITTED, ERROR_INVALID_ADDRESS,
+ NT_STATUS_NOT_EXPORT_FORMAT, ERROR_NOT_EXPORT_FORMAT,
+ NT_STATUS_NOT_FOUND, ERROR_NOT_FOUND,
+ NT_STATUS_NOT_IMPLEMENTED, ERROR_INVALID_FUNCTION,
+ NT_STATUS_NOT_LOCKED, ERROR_NOT_LOCKED,
+ NT_STATUS_NOT_LOGON_PROCESS, ERROR_NOT_LOGON_PROCESS,
+ NT_STATUS_NOT_MAPPED_DATA, ERROR_INVALID_ADDRESS,
+ NT_STATUS_NOT_MAPPED_VIEW, ERROR_INVALID_ADDRESS,
+ NT_STATUS_NOT_REGISTRY_FILE, ERROR_NOT_REGISTRY_FILE,
+ NT_STATUS_NOT_SAME_DEVICE, ERROR_NOT_SAME_DEVICE,
+ NT_STATUS_NOT_SERVER_SESSION, ERROR_NOT_SUPPORTED,
+ NT_STATUS_NOT_SUPPORTED, ERROR_NOT_SUPPORTED,
+ NT_STATUS_NOT_SUPPORTED_ON_SBS, ERROR_NOT_SUPPORTED_ON_SBS,
+ NT_STATUS_NO_BROWSER_SERVERS_FOUND, ERROR_NO_BROWSER_SERVERS_FOUND,
+ NT_STATUS_NO_DATA_DETECTED, ERROR_NO_DATA_DETECTED,
+ NT_STATUS_NO_EAS_ON_FILE, ERROR_FILE_CORRUPT,
+ NT_STATUS_NO_EFS, ERROR_ACCESS_DENIED,
+ NT_STATUS_NO_IMPERSONATION_TOKEN, ERROR_NO_IMPERSONATION_TOKEN,
+ NT_STATUS_NO_INHERITANCE, ERROR_NO_INHERITANCE,
+ /* NT_STATUS_NO_IP_ADDRESSES, SEC_E_NO_IP_ADDRESSES, */
+ /* NT_STATUS_NO_KERB_KEY, SEC_E_NO_KERB_KEY, */
+ NT_STATUS_NO_LDT, ERROR_INVALID_THREAD_ID,
+ NT_STATUS_NO_LOGON_SERVERS, ERROR_NO_LOGON_SERVERS,
+ NT_STATUS_NO_LOG_SPACE, ERROR_NO_LOG_SPACE,
+ NT_STATUS_NO_MATCH, ERROR_NO_MATCH,
+ NT_STATUS_NO_MEDIA, ERROR_NO_MEDIA_IN_DRIVE,
+ NT_STATUS_NO_MEDIA_IN_DEVICE, ERROR_NOT_READY,
+ NT_STATUS_NO_MEMORY, ERROR_NOT_ENOUGH_MEMORY,
+ NT_STATUS_NO_MORE_EAS, ERROR_NO_MORE_ITEMS,
+ NT_STATUS_NO_MORE_ENTRIES, ERROR_NO_MORE_ITEMS,
+ NT_STATUS_NO_MORE_FILES, ERROR_NO_MORE_FILES,
+ /* NT_STATUS_NO_PA_DATA, SEC_E_NO_PA_DATA, */
+ NT_STATUS_NO_QUOTAS_FOR_ACCOUNT, ERROR_NO_QUOTAS_FOR_ACCOUNT,
+ NT_STATUS_NO_RECOVERY_POLICY, ERROR_ACCESS_DENIED,
+ NT_STATUS_NO_SECURITY_ON_OBJECT, ERROR_NO_SECURITY_ON_OBJECT,
+ NT_STATUS_NO_SPOOL_SPACE, ERROR_NO_SPOOL_SPACE,
+ NT_STATUS_NO_SUCH_ALIAS, ERROR_NO_SUCH_ALIAS,
+ NT_STATUS_NO_SUCH_DEVICE, ERROR_FILE_NOT_FOUND,
+ NT_STATUS_NO_SUCH_DOMAIN, ERROR_NO_SUCH_DOMAIN,
+ NT_STATUS_NO_SUCH_FILE, ERROR_FILE_NOT_FOUND,
+ NT_STATUS_NO_SUCH_GROUP, ERROR_NO_SUCH_GROUP,
+ NT_STATUS_NO_SUCH_LOGON_SESSION, ERROR_NO_SUCH_LOGON_SESSION,
+ NT_STATUS_NO_SUCH_MEMBER, ERROR_NO_SUCH_MEMBER,
+ NT_STATUS_NO_SUCH_PACKAGE, ERROR_NO_SUCH_PACKAGE,
+ NT_STATUS_NO_SUCH_PRIVILEGE, ERROR_NO_SUCH_PRIVILEGE,
+ NT_STATUS_NO_SUCH_USER, ERROR_NO_SUCH_USER,
+ /* NT_STATUS_NO_TGT_REPLY, SEC_E_NO_TGT_REPLY, */
+ NT_STATUS_NO_TOKEN, ERROR_NO_TOKEN,
+ NT_STATUS_NO_TRACKING_SERVICE, ERROR_NO_TRACKING_SERVICE,
+ NT_STATUS_NO_TRUST_LSA_SECRET, ERROR_NO_TRUST_LSA_SECRET,
+ NT_STATUS_NO_TRUST_SAM_ACCOUNT, ERROR_NO_TRUST_SAM_ACCOUNT,
+ NT_STATUS_NO_USER_KEYS, ERROR_ACCESS_DENIED,
+ NT_STATUS_NO_USER_SESSION_KEY, ERROR_NO_USER_SESSION_KEY,
+ NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED, ERROR_NT_CROSS_ENCRYPTION_REQUIRED,
+ NT_STATUS_NULL_LM_PASSWORD, ERROR_NULL_LM_PASSWORD,
+ /* NT_STATUS_OBJECTID_EXISTS, STATUS_OBJECTID_EXISTS, */
+ NT_STATUS_OBJECTID_NOT_FOUND, ERROR_FILE_NOT_FOUND,
+ NT_STATUS_OBJECT_NAME_COLLISION, ERROR_ALREADY_EXISTS,
+ NT_STATUS_OBJECT_NAME_INVALID, ERROR_INVALID_NAME,
+ NT_STATUS_OBJECT_NAME_NOT_FOUND, ERROR_FILE_NOT_FOUND,
+ NT_STATUS_OBJECT_PATH_INVALID, ERROR_BAD_PATHNAME,
+ NT_STATUS_OBJECT_PATH_NOT_FOUND, ERROR_PATH_NOT_FOUND,
+ NT_STATUS_OBJECT_PATH_SYNTAX_BAD, ERROR_BAD_PATHNAME,
+ NT_STATUS_OBJECT_TYPE_MISMATCH, ERROR_INVALID_HANDLE,
+ NT_STATUS_ONLY_IF_CONNECTED, ERROR_ONLY_IF_CONNECTED,
+ NT_STATUS_OPEN_FAILED, ERROR_OPEN_FAILED,
+ NT_STATUS_OPLOCK_NOT_GRANTED, ERROR_OPLOCK_NOT_GRANTED,
+ NT_STATUS_ORDINAL_NOT_FOUND, ERROR_INVALID_ORDINAL,
+ NT_STATUS_PAGEFILE_QUOTA, ERROR_PAGEFILE_QUOTA,
+ NT_STATUS_PARTIAL_COPY, ERROR_PARTIAL_COPY,
+ NT_STATUS_PARTITION_FAILURE, ERROR_PARTITION_FAILURE,
+ NT_STATUS_PASSWORD_EXPIRED, ERROR_PASSWORD_EXPIRED,
+ NT_STATUS_PASSWORD_MUST_CHANGE, ERROR_PASSWORD_MUST_CHANGE,
+ NT_STATUS_PASSWORD_RESTRICTION, ERROR_PASSWORD_RESTRICTION,
+ NT_STATUS_PATH_NOT_COVERED, ERROR_HOST_UNREACHABLE,
+ NT_STATUS_PENDING, ERROR_IO_PENDING,
+ NT_STATUS_PIPE_BROKEN, ERROR_BROKEN_PIPE,
+ NT_STATUS_PIPE_BUSY, ERROR_PIPE_BUSY,
+ NT_STATUS_PIPE_CLOSING, ERROR_NO_DATA,
+ NT_STATUS_PIPE_CONNECTED, ERROR_PIPE_CONNECTED,
+ NT_STATUS_PIPE_DISCONNECTED, ERROR_PIPE_NOT_CONNECTED,
+ NT_STATUS_PIPE_EMPTY, ERROR_NO_DATA,
+ NT_STATUS_PIPE_LISTENING, ERROR_PIPE_LISTENING,
+ NT_STATUS_PIPE_NOT_AVAILABLE, ERROR_PIPE_BUSY,
+ NT_STATUS_PKINIT_CLIENT_FAILURE, SEC_E_PKINIT_CLIENT_FAILURE,
+ NT_STATUS_PKINIT_FAILURE, ERROR_PKINIT_FAILURE,
+ /* NT_STATUS_PKINIT_NAME_MISMATCH, SEC_E_PKINIT_NAME_MISMATCH, */
+ NT_STATUS_PLUGPLAY_NO_DEVICE, ERROR_SERVICE_DISABLED,
+ NT_STATUS_POLICY_OBJECT_NOT_FOUND, ERROR_POLICY_OBJECT_NOT_FOUND,
+ NT_STATUS_POLICY_ONLY_IN_DS, ERROR_POLICY_ONLY_IN_DS,
+ NT_STATUS_PORT_ALREADY_SET, ERROR_INVALID_PARAMETER,
+ NT_STATUS_PORT_CONNECTION_REFUSED, ERROR_ACCESS_DENIED,
+ NT_STATUS_PORT_DISCONNECTED, ERROR_INVALID_HANDLE,
+ NT_STATUS_PORT_UNREACHABLE, ERROR_PORT_UNREACHABLE,
+ NT_STATUS_POSSIBLE_DEADLOCK, ERROR_POSSIBLE_DEADLOCK,
+ NT_STATUS_PRENT4_MACHINE_ACCOUNT, ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4,
+ NT_STATUS_PRINT_CANCELLED, ERROR_PRINT_CANCELLED,
+ NT_STATUS_PRINT_QUEUE_FULL, ERROR_PRINTQ_FULL,
+ NT_STATUS_PRIVILEGE_NOT_HELD, ERROR_PRIVILEGE_NOT_HELD,
+ NT_STATUS_PROCEDURE_NOT_FOUND, ERROR_PROC_NOT_FOUND,
+ NT_STATUS_PROCESS_IS_TERMINATING, ERROR_ACCESS_DENIED,
+ NT_STATUS_PROPSET_NOT_FOUND, ERROR_SET_NOT_FOUND,
+ NT_STATUS_PROTOCOL_UNREACHABLE, ERROR_PROTOCOL_UNREACHABLE,
+ NT_STATUS_QUOTA_EXCEEDED, ERROR_NOT_ENOUGH_QUOTA,
+ NT_STATUS_RANGE_NOT_FOUND, ERROR_NOT_LOCKED,
+ NT_STATUS_RANGE_NOT_LOCKED, ERROR_NOT_LOCKED,
+ NT_STATUS_REDIRECTOR_NOT_STARTED, ERROR_PATH_NOT_FOUND,
+ NT_STATUS_REDIRECTOR_PAUSED, ERROR_REDIR_PAUSED,
+ NT_STATUS_REDIRECTOR_STARTED, ERROR_SERVICE_ALREADY_RUNNING,
+ NT_STATUS_REGISTRY_CORRUPT, ERROR_BADDB,
+ NT_STATUS_REGISTRY_IO_FAILED, ERROR_REGISTRY_IO_FAILED,
+ NT_STATUS_REGISTRY_RECOVERED, ERROR_REGISTRY_RECOVERED,
+ NT_STATUS_REG_NAT_CONSUMPTION, ERROR_REG_NAT_CONSUMPTION,
+ NT_STATUS_REINITIALIZATION_NEEDED, ERROR_DEVICE_REINITIALIZATION_NEEDED,
+ NT_STATUS_REMOTE_DISCONNECT, ERROR_NETNAME_DELETED,
+ NT_STATUS_REMOTE_NOT_LISTENING, ERROR_REM_NOT_LIST,
+ NT_STATUS_REMOTE_RESOURCES, ERROR_REM_NOT_LIST,
+ NT_STATUS_REMOTE_SESSION_LIMIT, ERROR_REMOTE_SESSION_LIMIT_EXCEEDED,
+ NT_STATUS_REMOTE_STORAGE_MEDIA_ERROR, ERROR_REMOTE_STORAGE_MEDIA_ERROR,
+ NT_STATUS_REMOTE_STORAGE_NOT_ACTIVE, ERROR_REMOTE_STORAGE_NOT_ACTIVE,
+ NT_STATUS_REPARSE_ATTRIBUTE_CONFLICT, ERROR_REPARSE_ATTRIBUTE_CONFLICT,
+ NT_STATUS_REPARSE_POINT_NOT_RESOLVED, ERROR_CANT_RESOLVE_FILENAME,
+ NT_STATUS_REQUEST_ABORTED, ERROR_REQUEST_ABORTED,
+ NT_STATUS_REQUEST_NOT_ACCEPTED, ERROR_REQ_NOT_ACCEP,
+ NT_STATUS_RESOURCE_DATA_NOT_FOUND, ERROR_RESOURCE_DATA_NOT_FOUND,
+ NT_STATUS_RESOURCE_LANG_NOT_FOUND, ERROR_RESOURCE_LANG_NOT_FOUND,
+ NT_STATUS_RESOURCE_NAME_NOT_FOUND, ERROR_RESOURCE_NAME_NOT_FOUND,
+ NT_STATUS_RESOURCE_NOT_OWNED, ERROR_NOT_OWNER,
+ NT_STATUS_RESOURCE_TYPE_NOT_FOUND, ERROR_RESOURCE_TYPE_NOT_FOUND,
+ NT_STATUS_RETRY, ERROR_RETRY,
+ NT_STATUS_REVISION_MISMATCH, ERROR_REVISION_MISMATCH,
+ NT_STATUS_REVOCATION_OFFLINE_C, SEC_E_REVOCATION_OFFLINE_C,
+ NT_STATUS_RXACT_COMMIT_FAILURE, ERROR_RXACT_COMMIT_FAILURE,
+ NT_STATUS_RXACT_INVALID_STATE, ERROR_RXACT_INVALID_STATE,
+ NT_STATUS_SAM_INIT_FAILURE, ERROR_SAM_INIT_FAILURE,
+ NT_STATUS_SAM_NEED_BOOTKEY_FLOPPY, ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY,
+ NT_STATUS_SAM_NEED_BOOTKEY_PASSWORD, ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD,
+ NT_STATUS_SECRET_TOO_LONG, ERROR_SECRET_TOO_LONG,
+ NT_STATUS_SECTION_NOT_EXTENDED, ERROR_OUTOFMEMORY,
+ NT_STATUS_SECTION_NOT_IMAGE, ERROR_INVALID_PARAMETER,
+ NT_STATUS_SECTION_PROTECTION, ERROR_INVALID_PARAMETER,
+ NT_STATUS_SECTION_TOO_BIG, ERROR_NOT_ENOUGH_MEMORY,
+ NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED, ERROR_TOO_MANY_POSTS,
+ NT_STATUS_SERIAL_COUNTER_TIMEOUT, ERROR_COUNTER_TIMEOUT,
+ NT_STATUS_SERIAL_MORE_WRITES, ERROR_MORE_WRITES,
+ NT_STATUS_SERIAL_NO_DEVICE_INITED, ERROR_SERIAL_NO_DEVICE,
+ NT_STATUS_SERVER_DISABLED, ERROR_SERVER_DISABLED,
+ NT_STATUS_SERVER_NOT_DISABLED, ERROR_SERVER_NOT_DISABLED,
+ NT_STATUS_SERVER_SHUTDOWN_IN_PROGRESS, ERROR_SERVER_SHUTDOWN_IN_PROGRESS,
+ NT_STATUS_SETMARK_DETECTED, ERROR_SETMARK_DETECTED,
+ NT_STATUS_SHARED_IRQ_BUSY, ERROR_IRQ_BUSY,
+ NT_STATUS_SHARED_POLICY, ERROR_SHARED_POLICY,
+ NT_STATUS_SHARING_PAUSED, ERROR_SHARING_PAUSED,
+ NT_STATUS_SHARING_VIOLATION, ERROR_SHARING_VIOLATION,
+ NT_STATUS_SHUTDOWN_IN_PROGRESS, ERROR_SHUTDOWN_IN_PROGRESS,
+ /* NT_STATUS_SMARTCARD_... */
+ NT_STATUS_SOME_NOT_MAPPED, ERROR_SOME_NOT_MAPPED,
+ NT_STATUS_SOURCE_ELEMENT_EMPTY, ERROR_SOURCE_ELEMENT_EMPTY,
+ NT_STATUS_SPECIAL_ACCOUNT, ERROR_SPECIAL_ACCOUNT,
+ NT_STATUS_SPECIAL_GROUP, ERROR_SPECIAL_GROUP,
+ NT_STATUS_SPECIAL_USER, ERROR_SPECIAL_USER,
+ NT_STATUS_STACK_OVERFLOW, ERROR_STACK_OVERFLOW,
+ /* NT_STATUS_STRONG_CRYPTO_NOT_SUPPORTED, SEC_E_STRONG_CRYPTO_NOT_SUPPORTED, */
+ NT_STATUS_SUSPEND_COUNT_EXCEEDED, ERROR_SIGNAL_REFUSED,
+ /* NT_STATUS_SXS_... */
+ NT_STATUS_THREAD_IS_TERMINATING, ERROR_ACCESS_DENIED,
+ NT_STATUS_TIME_DIFFERENCE_AT_DC, ERROR_TIME_SKEW,
+ NT_STATUS_TOKEN_ALREADY_IN_USE, ERROR_TOKEN_ALREADY_IN_USE,
+ NT_STATUS_TOO_LATE, ERROR_WRITE_PROTECT,
+ NT_STATUS_TOO_MANY_ADDRESSES, ERROR_TOO_MANY_NAMES,
+ NT_STATUS_TOO_MANY_COMMANDS, ERROR_TOO_MANY_CMDS,
+ NT_STATUS_TOO_MANY_CONTEXT_IDS, ERROR_TOO_MANY_CONTEXT_IDS,
+ NT_STATUS_TOO_MANY_GUIDS_REQUESTED, ERROR_TOO_MANY_NAMES,
+ NT_STATUS_TOO_MANY_LINKS, ERROR_TOO_MANY_LINKS,
+ NT_STATUS_TOO_MANY_LUIDS_REQUESTED, ERROR_TOO_MANY_LUIDS_REQUESTED,
+ NT_STATUS_TOO_MANY_NAMES, ERROR_TOO_MANY_NAMES,
+ NT_STATUS_TOO_MANY_NODES, ERROR_TOO_MANY_NAMES,
+ NT_STATUS_TOO_MANY_OPENED_FILES, ERROR_TOO_MANY_OPEN_FILES,
+ NT_STATUS_TOO_MANY_PAGING_FILES, ERROR_NOT_ENOUGH_MEMORY,
+ /* NT_STATUS_TOO_MANY_PRINCIPALS, SEC_E_TOO_MANY_PRINCIPALS, */
+ NT_STATUS_TOO_MANY_SECRETS, ERROR_TOO_MANY_SECRETS,
+ NT_STATUS_TOO_MANY_SESSIONS, ERROR_TOO_MANY_SESS,
+ NT_STATUS_TOO_MANY_SIDS, ERROR_TOO_MANY_SIDS,
+ NT_STATUS_TRANSACTION_ABORTED, ERROR_UNEXP_NET_ERR,
+ NT_STATUS_TRANSACTION_INVALID_ID, ERROR_UNEXP_NET_ERR,
+ NT_STATUS_TRANSACTION_INVALID_TYPE, ERROR_UNEXP_NET_ERR,
+ NT_STATUS_TRANSACTION_NO_MATCH, ERROR_UNEXP_NET_ERR,
+ NT_STATUS_TRANSACTION_NO_RELEASE, ERROR_UNEXP_NET_ERR,
+ NT_STATUS_TRANSACTION_RESPONDED, ERROR_UNEXP_NET_ERR,
+ NT_STATUS_TRANSACTION_TIMED_OUT, ERROR_UNEXP_NET_ERR,
+ NT_STATUS_TRANSPORT_FULL, ERROR_TRANSPORT_FULL,
+ NT_STATUS_TRUSTED_DOMAIN_FAILURE, ERROR_TRUSTED_DOMAIN_FAILURE,
+ NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE, ERROR_TRUSTED_RELATIONSHIP_FAILURE,
+ NT_STATUS_TRUST_FAILURE, ERROR_TRUST_FAILURE,
+ NT_STATUS_UNABLE_TO_DECOMMIT_VM, ERROR_INVALID_ADDRESS,
+ NT_STATUS_UNABLE_TO_DELETE_SECTION, ERROR_INVALID_PARAMETER,
+ NT_STATUS_UNABLE_TO_FREE_VM, ERROR_INVALID_PARAMETER,
+ NT_STATUS_UNABLE_TO_LOCK_MEDIA, ERROR_UNABLE_TO_LOCK_MEDIA,
+ NT_STATUS_UNABLE_TO_UNLOAD_MEDIA, ERROR_UNABLE_TO_UNLOAD_MEDIA,
+ NT_STATUS_UNDEFINED_CHARACTER, ERROR_NO_UNICODE_TRANSLATION,
+ NT_STATUS_UNEXPECTED_NETWORK_ERROR, ERROR_UNEXP_NET_ERR,
+ /* NT_STATUS_UNFINISHED_CONTEXT_DELETED, SEC_E_UNFINISHED_CONTEXT_DELETED, */
+ NT_STATUS_UNKNOWN_REVISION, ERROR_UNKNOWN_REVISION,
+ NT_STATUS_UNMAPPABLE_CHARACTER, ERROR_NO_UNICODE_TRANSLATION,
+ NT_STATUS_UNRECOGNIZED_MEDIA, ERROR_UNRECOGNIZED_MEDIA,
+ NT_STATUS_UNRECOGNIZED_VOLUME, ERROR_UNRECOGNIZED_VOLUME,
+ NT_STATUS_UNSUCCESSFUL, ERROR_GEN_FAILURE,
+ /* NT_STATUS_UNSUPPORTED_PREAUTH, SEC_E_UNSUPPORTED_PREAUTH, */
+ NT_STATUS_USER_EXISTS, ERROR_USER_EXISTS,
+ NT_STATUS_USER_MAPPED_FILE, ERROR_USER_MAPPED_FILE,
+ NT_STATUS_USER_SESSION_DELETED, ERROR_UNEXP_NET_ERR,
+ NT_STATUS_VARIABLE_NOT_FOUND, ERROR_ENVVAR_NOT_FOUND,
+ NT_STATUS_VERIFY_REQUIRED, ERROR_MEDIA_CHANGED,
+ NT_STATUS_VIRTUAL_CIRCUIT_CLOSED, ERROR_VC_DISCONNECTED,
+ NT_STATUS_VOLUME_DISMOUNTED, ERROR_NOT_READY,
+ NT_STATUS_VOLUME_NOT_UPGRADED, ERROR_INVALID_FUNCTION,
+ NT_STATUS_WMI_ALREADY_DISABLED, ERROR_WMI_ALREADY_DISABLED,
+ NT_STATUS_WMI_ALREADY_ENABLED, ERROR_WMI_ALREADY_ENABLED,
+ NT_STATUS_WMI_GUID_DISCONNECTED, ERROR_WMI_GUID_DISCONNECTED,
+ NT_STATUS_WMI_GUID_NOT_FOUND, ERROR_WMI_GUID_NOT_FOUND,
+ NT_STATUS_WMI_INSTANCE_NOT_FOUND, ERROR_WMI_INSTANCE_NOT_FOUND,
+ NT_STATUS_WMI_ITEMID_NOT_FOUND, ERROR_WMI_ITEMID_NOT_FOUND,
+ NT_STATUS_WMI_NOT_SUPPORTED, ERROR_NOT_SUPPORTED,
+ NT_STATUS_WMI_READ_ONLY, ERROR_WMI_READ_ONLY,
+ NT_STATUS_WMI_SET_FAILURE, ERROR_WMI_SET_FAILURE,
+ NT_STATUS_WMI_TRY_AGAIN, ERROR_WMI_TRY_AGAIN,
+ NT_STATUS_WORKING_SET_LIMIT_RANGE, ERROR_INVALID_PARAMETER,
+ NT_STATUS_WORKING_SET_QUOTA, ERROR_WORKING_SET_QUOTA,
+ /* NT_STATUS_WRONG_CREDENTIAL_HANDLE, SEC_E_WRONG_CREDENTIAL_HANDLE, */
+ NT_STATUS_WRONG_EFS, ERROR_ACCESS_DENIED,
+ NT_STATUS_WRONG_PASSWORD, ERROR_INVALID_PASSWORD,
+ NT_STATUS_WRONG_PASSWORD_CORE, ERROR_INVALID_PASSWORD,
+ NT_STATUS_WRONG_VOLUME, ERROR_WRONG_DISK,
+
+ EPT_NT_CANT_CREATE, EPT_S_CANT_CREATE,
+ EPT_NT_CANT_PERFORM_OP, EPT_S_CANT_PERFORM_OP,
+ EPT_NT_INVALID_ENTRY, EPT_S_INVALID_ENTRY,
+ EPT_NT_NOT_REGISTERED, EPT_S_NOT_REGISTERED,
+
+ RPC_NT_ADDRESS_ERROR, RPC_S_ADDRESS_ERROR,
+ RPC_NT_ALREADY_LISTENING, RPC_S_ALREADY_LISTENING,
+ RPC_NT_ALREADY_REGISTERED, RPC_S_ALREADY_REGISTERED,
+ RPC_NT_BAD_STUB_DATA, RPC_X_BAD_STUB_DATA,
+ RPC_NT_BINDING_HAS_NO_AUTH, RPC_S_BINDING_HAS_NO_AUTH,
+ RPC_NT_BINDING_INCOMPLETE, RPC_S_BINDING_INCOMPLETE,
+ RPC_NT_BYTE_COUNT_TOO_SMALL, RPC_X_BYTE_COUNT_TOO_SMALL,
+ RPC_NT_CALL_CANCELLED, RPC_S_CALL_CANCELLED,
+ RPC_NT_CALL_FAILED, RPC_S_CALL_FAILED,
+ RPC_NT_CALL_FAILED_DNE, RPC_S_CALL_FAILED_DNE,
+ RPC_NT_CALL_IN_PROGRESS, RPC_S_CALL_IN_PROGRESS,
+ RPC_NT_CANNOT_SUPPORT, RPC_S_CANNOT_SUPPORT,
+ RPC_NT_CANT_CREATE_ENDPOINT, RPC_S_CANT_CREATE_ENDPOINT,
+ RPC_NT_COMM_FAILURE, RPC_S_COMM_FAILURE,
+ RPC_NT_DUPLICATE_ENDPOINT, RPC_S_DUPLICATE_ENDPOINT,
+ RPC_NT_ENTRY_ALREADY_EXISTS, RPC_S_ENTRY_ALREADY_EXISTS,
+ RPC_NT_ENTRY_NOT_FOUND, RPC_S_ENTRY_NOT_FOUND,
+ RPC_NT_ENUM_VALUE_OUT_OF_RANGE, RPC_X_ENUM_VALUE_OUT_OF_RANGE,
+ RPC_NT_FP_DIV_ZERO, RPC_S_FP_DIV_ZERO,
+ RPC_NT_FP_OVERFLOW, RPC_S_FP_OVERFLOW,
+ RPC_NT_FP_UNDERFLOW, RPC_S_FP_UNDERFLOW,
+ RPC_NT_GROUP_MEMBER_NOT_FOUND, RPC_S_GROUP_MEMBER_NOT_FOUND,
+ RPC_NT_INCOMPLETE_NAME, RPC_S_INCOMPLETE_NAME,
+ RPC_NT_INTERFACE_NOT_FOUND, RPC_S_INTERFACE_NOT_FOUND,
+ RPC_NT_INTERNAL_ERROR, RPC_S_INTERNAL_ERROR,
+ RPC_NT_INVALID_ASYNC_CALL, RPC_S_INVALID_ASYNC_CALL,
+ RPC_NT_INVALID_ASYNC_HANDLE, RPC_S_INVALID_ASYNC_HANDLE,
+ RPC_NT_INVALID_AUTH_IDENTITY, RPC_S_INVALID_AUTH_IDENTITY,
+ RPC_NT_INVALID_BINDING, ERROR_INVALID_HANDLE,
+ RPC_NT_INVALID_BOUND, RPC_S_INVALID_BOUND,
+ RPC_NT_INVALID_ENDPOINT_FORMAT, RPC_S_INVALID_ENDPOINT_FORMAT,
+ RPC_NT_INVALID_ES_ACTION, RPC_X_INVALID_ES_ACTION,
+ RPC_NT_INVALID_NAF_ID, RPC_S_INVALID_NAF_ID,
+ RPC_NT_INVALID_NAME_SYNTAX, RPC_S_INVALID_NAME_SYNTAX,
+ RPC_NT_INVALID_NETWORK_OPTIONS, RPC_S_INVALID_NETWORK_OPTIONS,
+ RPC_NT_INVALID_NET_ADDR, RPC_S_INVALID_NET_ADDR,
+ RPC_NT_INVALID_OBJECT, RPC_S_INVALID_OBJECT,
+ RPC_NT_INVALID_PIPE_OBJECT, RPC_X_INVALID_PIPE_OBJECT,
+ RPC_NT_INVALID_PIPE_OPERATION, RPC_X_WRONG_PIPE_ORDER,
+ RPC_NT_INVALID_RPC_PROTSEQ, RPC_S_INVALID_RPC_PROTSEQ,
+ RPC_NT_INVALID_STRING_BINDING, RPC_S_INVALID_STRING_BINDING,
+ RPC_NT_INVALID_STRING_UUID, RPC_S_INVALID_STRING_UUID,
+ RPC_NT_INVALID_TAG, RPC_S_INVALID_TAG,
+ RPC_NT_INVALID_TIMEOUT, RPC_S_INVALID_TIMEOUT,
+ RPC_NT_INVALID_VERS_OPTION, RPC_S_INVALID_VERS_OPTION,
+ RPC_NT_MAX_CALLS_TOO_SMALL, RPC_S_MAX_CALLS_TOO_SMALL,
+ RPC_NT_NAME_SERVICE_UNAVAILABLE, RPC_S_NAME_SERVICE_UNAVAILABLE,
+ RPC_NT_NOTHING_TO_EXPORT, RPC_S_NOTHING_TO_EXPORT,
+ RPC_NT_NOT_ALL_OBJS_UNEXPORTED, RPC_S_NOT_ALL_OBJS_UNEXPORTED,
+ RPC_NT_NOT_CANCELLED, RPC_S_NOT_CANCELLED,
+ RPC_NT_NOT_LISTENING, RPC_S_NOT_LISTENING,
+ RPC_NT_NOT_RPC_ERROR, RPC_S_NOT_RPC_ERROR,
+ RPC_NT_NO_BINDINGS, RPC_S_NO_BINDINGS,
+ RPC_NT_NO_CALL_ACTIVE, RPC_S_NO_CALL_ACTIVE,
+ RPC_NT_NO_CONTEXT_AVAILABLE, RPC_S_NO_CONTEXT_AVAILABLE,
+ RPC_NT_NO_ENDPOINT_FOUND, RPC_S_NO_ENDPOINT_FOUND,
+ RPC_NT_NO_ENTRY_NAME, RPC_S_NO_ENTRY_NAME,
+ RPC_NT_NO_INTERFACES, RPC_S_NO_INTERFACES,
+ RPC_NT_NO_MORE_BINDINGS, RPC_S_NO_MORE_BINDINGS,
+ RPC_NT_NO_MORE_ENTRIES, RPC_X_NO_MORE_ENTRIES,
+ RPC_NT_NO_MORE_MEMBERS, RPC_S_NO_MORE_MEMBERS,
+ RPC_NT_NO_PRINC_NAME, RPC_S_NO_PRINC_NAME,
+ RPC_NT_NO_PROTSEQS, RPC_S_NO_PROTSEQS,
+ RPC_NT_NO_PROTSEQS_REGISTERED, RPC_S_NO_PROTSEQS_REGISTERED,
+ RPC_NT_NULL_REF_POINTER, RPC_X_NULL_REF_POINTER,
+ RPC_NT_OBJECT_NOT_FOUND, RPC_S_OBJECT_NOT_FOUND,
+ RPC_NT_OUT_OF_RESOURCES, RPC_S_OUT_OF_RESOURCES,
+ RPC_NT_PIPE_CLOSED, RPC_X_PIPE_CLOSED,
+ RPC_NT_PIPE_DISCIPLINE_ERROR, RPC_X_PIPE_DISCIPLINE_ERROR,
+ RPC_NT_PIPE_EMPTY, RPC_X_PIPE_EMPTY,
+ RPC_NT_PROCNUM_OUT_OF_RANGE, RPC_S_PROCNUM_OUT_OF_RANGE,
+ RPC_NT_PROTOCOL_ERROR, RPC_S_PROTOCOL_ERROR,
+ RPC_NT_PROTSEQ_NOT_FOUND, RPC_S_PROTSEQ_NOT_FOUND,
+ RPC_NT_PROTSEQ_NOT_SUPPORTED, RPC_S_PROTSEQ_NOT_SUPPORTED,
+ RPC_NT_SEC_PKG_ERROR, RPC_S_SEC_PKG_ERROR,
+ RPC_NT_SEND_INCOMPLETE, RPC_S_SEND_INCOMPLETE,
+ RPC_NT_SERVER_TOO_BUSY, RPC_S_SERVER_TOO_BUSY,
+ RPC_NT_SERVER_UNAVAILABLE, RPC_S_SERVER_UNAVAILABLE,
+ RPC_NT_SS_CANNOT_GET_CALL_HANDLE, RPC_X_SS_CANNOT_GET_CALL_HANDLE,
+ RPC_NT_SS_CHAR_TRANS_OPEN_FAIL, RPC_X_SS_CHAR_TRANS_OPEN_FAIL,
+ RPC_NT_SS_CHAR_TRANS_SHORT_FILE, RPC_X_SS_CHAR_TRANS_SHORT_FILE,
+ RPC_NT_SS_CONTEXT_DAMAGED, RPC_X_SS_CONTEXT_DAMAGED,
+ RPC_NT_SS_CONTEXT_MISMATCH, ERROR_INVALID_HANDLE,
+ RPC_NT_SS_HANDLES_MISMATCH, RPC_X_SS_HANDLES_MISMATCH,
+ RPC_NT_SS_IN_NULL_CONTEXT, ERROR_INVALID_HANDLE,
+ RPC_NT_STRING_TOO_LONG, RPC_S_STRING_TOO_LONG,
+ RPC_NT_TYPE_ALREADY_REGISTERED, RPC_S_TYPE_ALREADY_REGISTERED,
+ RPC_NT_UNKNOWN_AUTHN_LEVEL, RPC_S_UNKNOWN_AUTHN_LEVEL,
+ RPC_NT_UNKNOWN_AUTHN_SERVICE, RPC_S_UNKNOWN_AUTHN_SERVICE,
+ RPC_NT_UNKNOWN_AUTHN_TYPE, RPC_S_UNKNOWN_AUTHN_TYPE,
+ RPC_NT_UNKNOWN_AUTHZ_SERVICE, RPC_S_UNKNOWN_AUTHZ_SERVICE,
+ RPC_NT_UNKNOWN_IF, RPC_S_UNKNOWN_IF,
+ RPC_NT_UNKNOWN_MGR_TYPE, RPC_S_UNKNOWN_MGR_TYPE,
+ RPC_NT_UNSUPPORTED_AUTHN_LEVEL, RPC_S_UNSUPPORTED_AUTHN_LEVEL,
+ RPC_NT_UNSUPPORTED_NAME_SYNTAX, RPC_S_UNSUPPORTED_NAME_SYNTAX,
+ RPC_NT_UNSUPPORTED_TRANS_SYN, RPC_S_UNSUPPORTED_TRANS_SYN,
+ RPC_NT_UNSUPPORTED_TYPE, RPC_S_UNSUPPORTED_TYPE,
+ RPC_NT_UUID_LOCAL_ONLY, RPC_S_UUID_LOCAL_ONLY,
+ RPC_NT_UUID_NO_ADDRESS, RPC_S_UUID_NO_ADDRESS,
+ RPC_NT_WRONG_ES_VERSION, RPC_X_WRONG_ES_VERSION,
+ RPC_NT_WRONG_KIND_OF_BINDING, RPC_S_WRONG_KIND_OF_BINDING,
+ RPC_NT_WRONG_PIPE_VERSION, RPC_X_WRONG_PIPE_VERSION,
+ RPC_NT_WRONG_STUB_VERSION, RPC_X_WRONG_STUB_VERSION,
+ RPC_NT_ZERO_DIVIDE, RPC_S_ZERO_DIVIDE,
+
+ /* END CSTYLED */
+ 0, 0
+};
diff --git a/usr/src/common/smbclnt/smb_status2winerr.h b/usr/src/common/smbclnt/smb_status2winerr.h
new file mode 100644
index 0000000000..a7ff5251ec
--- /dev/null
+++ b/usr/src/common/smbclnt/smb_status2winerr.h
@@ -0,0 +1,37 @@
+/*
+ * 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 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#ifndef _SMB_STATUS2WINERR_H_
+#define _SMB_STATUS2WINERR_H_
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct status2winerr {
+ uint_t status;
+ uint_t winerr;
+};
+
+/* This is a zero-terminated table. */
+extern const struct status2winerr smb_status2winerr_map[];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMB_STATUS2WINERR_H_ */
diff --git a/usr/src/common/smbsrv/smb_xdr.c b/usr/src/common/smbsrv/smb_xdr.c
index e7640554aa..0905e48528 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 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/sunddi.h>
@@ -449,6 +449,9 @@ smb_gmttoken_snapname_xdr(XDR *xdrs, smb_gmttoken_snapname_t *objp)
if (!xdr_string(xdrs, &objp->gts_gmttoken, SMB_VSS_GMT_SIZE)) {
return (FALSE);
}
+ if (!xdr_uint64_t(xdrs, &objp->gts_toktime)) {
+ return (FALSE);
+ }
return (TRUE);
}
diff --git a/usr/src/lib/libfakekernel/common/clock.c b/usr/src/lib/libfakekernel/common/clock.c
index dde86bdcfd..114190413c 100644
--- a/usr/src/lib/libfakekernel/common/clock.c
+++ b/usr/src/lib/libfakekernel/common/clock.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
@@ -72,11 +72,11 @@ gethrtime_unscaled(void)
void
gethrestime(timespec_t *ts)
{
- hrtime_t hrt;
+ struct timeval tv;
- hrt = gethrtime();
- ts->tv_sec = hrt / NANOSEC;
- ts->tv_nsec = hrt % NANOSEC;
+ (void) gettimeofday(&tv, NULL);
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_nsec = tv.tv_usec * 1000;
}
time_t
diff --git a/usr/src/lib/libfakekernel/common/ksocket.c b/usr/src/lib/libfakekernel/common/ksocket.c
index 5ff3538926..3543f76a85 100644
--- a/usr/src/lib/libfakekernel/common/ksocket.c
+++ b/usr/src/lib/libfakekernel/common/ksocket.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2008, 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/types.h>
@@ -32,6 +32,7 @@
#include <sys/ksocket.h>
#include <sys/debug.h>
#include <sys/kmem.h>
+#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include <umem.h>
@@ -293,7 +294,8 @@ int
ksocket_sendmsg(ksocket_t ks, struct nmsghdr *msg, int flags,
size_t *sent, struct cred *cr)
{
- ssize_t error;
+ uio_t uio;
+ ssize_t len;
/* All Solaris components should pass a cred for this operation. */
ASSERT(cr != NULL);
@@ -304,15 +306,35 @@ ksocket_sendmsg(ksocket_t ks, struct nmsghdr *msg, int flags,
return (ENOTSOCK);
}
- error = sendmsg(KSTOSO(ks), msg, flags);
- if (error < 0) {
+ /* socksyscalls.c uses MSG_MAXIOVLEN (local macro), both are 16 */
+ ASSERT3U(msg->msg_iovlen, <=, IOV_MAX);
+ len = sendmsg(KSTOSO(ks), msg, flags);
+ if (len < 0) {
if (sent != NULL)
*sent = 0;
return (errno);
}
+ /*
+ * The user-level sendmsg() does NOT update msg->iov like
+ * ksocket_sendmsg(). It's unclear whether that's a bug
+ * or if that was intentional. Anyway, update it here.
+ */
+ if (msg->msg_iov != NULL) {
+ bzero(&uio, sizeof (uio));
+ uio.uio_iov = msg->msg_iov;
+ uio.uio_iovcnt = msg->msg_iovlen;
+ uio.uio_resid = len;
+
+ uioskip(&uio, len);
+ ASSERT(uio.uio_resid == 0);
+
+ msg->msg_iov = uio.uio_iov;
+ msg->msg_iovlen = uio.uio_iovcnt;
+ }
+
if (sent != NULL)
- *sent = (size_t)error;
+ *sent = (size_t)len;
return (0);
}
diff --git a/usr/src/lib/libfakekernel/common/mapfile-vers b/usr/src/lib/libfakekernel/common/mapfile-vers
index ffbff01f7f..db81dea59a 100644
--- a/usr/src/lib/libfakekernel/common/mapfile-vers
+++ b/usr/src/lib/libfakekernel/common/mapfile-vers
@@ -10,7 +10,7 @@
#
#
-# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
#
#
@@ -180,6 +180,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
tick_per_msec;
tsignal;
uiomove;
+ uioskip;
usec_per_tick;
vcmn_err;
vmem_qcache_reap;
diff --git a/usr/src/lib/libfakekernel/common/uio.c b/usr/src/lib/libfakekernel/common/uio.c
index 934c8900a8..3048faff58 100644
--- a/usr/src/lib/libfakekernel/common/uio.c
+++ b/usr/src/lib/libfakekernel/common/uio.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -62,3 +62,28 @@ uiomove(void *p, size_t n, enum uio_rw rw, struct uio *uio)
}
return (0);
}
+
+/*
+ * Drop the next n chars out of *uiop.
+ */
+void
+uioskip(uio_t *uiop, size_t n)
+{
+ if (n > uiop->uio_resid)
+ return;
+ while (n != 0) {
+ iovec_t *iovp = uiop->uio_iov;
+ size_t niovb = MIN(iovp->iov_len, n);
+
+ if (niovb == 0) {
+ uiop->uio_iov++;
+ uiop->uio_iovcnt--;
+ continue;
+ }
+ iovp->iov_base += niovb;
+ uiop->uio_loffset += niovb;
+ iovp->iov_len -= niovb;
+ uiop->uio_resid -= niovb;
+ n -= niovb;
+ }
+}
diff --git a/usr/src/lib/libshare/smb/libshare_smb.c b/usr/src/lib/libshare/smb/libshare_smb.c
index 0d087329f7..1d3b04cf9a 100644
--- a/usr/src/lib/libshare/smb/libshare_smb.c
+++ b/usr/src/lib/libshare/smb/libshare_smb.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 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -84,6 +84,7 @@ static int hostname_validator(int, char *);
static int path_validator(int, char *);
static int cmd_validator(int, char *);
static int disposition_validator(int, char *);
+static int max_protocol_validator(int, char *);
static int smb_enable_resource(sa_resource_t);
static int smb_disable_resource(sa_resource_t);
@@ -875,7 +876,9 @@ struct smb_proto_option_defs {
} smb_proto_options[] = {
{ SMB_CI_SYS_CMNT, 0, MAX_VALUE_BUFLEN,
string_length_check_validator, SMB_REFRESH_REFRESH },
- { SMB_CI_MAX_WORKERS, 64, 1024, range_check_validator,
+ { SMB_CI_MAX_WORKERS, SMB_PI_MAX_WORKERS_MIN, SMB_PI_MAX_WORKERS_MAX,
+ range_check_validator, SMB_REFRESH_REFRESH },
+ { SMB_CI_NETBIOS_ENABLE, 0, 0, true_false_validator,
SMB_REFRESH_REFRESH },
{ SMB_CI_NBSCOPE, 0, MAX_VALUE_BUFLEN,
string_length_check_validator, 0 },
@@ -911,7 +914,7 @@ struct smb_proto_option_defs {
SMB_REFRESH_REFRESH },
{ SMB_CI_DISPOSITION, 0, MAX_VALUE_BUFLEN,
disposition_validator, SMB_REFRESH_REFRESH },
- { SMB_CI_NETBIOS_ENABLE, 0, 0, true_false_validator,
+ { SMB_CI_MAX_PROTOCOL, 0, MAX_VALUE_BUFLEN, max_protocol_validator,
SMB_REFRESH_REFRESH },
};
@@ -2342,6 +2345,23 @@ disposition_validator(int index, char *value)
return (SA_BAD_VALUE);
}
+/*ARGSUSED*/
+static int
+max_protocol_validator(int index, char *value)
+{
+ if (value == NULL)
+ return (SA_BAD_VALUE);
+
+ if (*value == '\0')
+ return (SA_OK);
+
+ if (smb_config_check_protocol(value) == 0)
+ return (SA_OK);
+
+ return (SA_BAD_VALUE);
+
+}
+
/*
* Updates the optionset properties of the share resource.
* The properties are given as a list of name-value pair.
diff --git a/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com b/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com
index 20c8f74f77..cfc1776993 100644
--- a/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com
+++ b/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com
@@ -30,6 +30,7 @@ VERS = .1
OBJS_LOCAL = \
fksmb_cred.o \
+ fksmb_dt.o \
fksmb_fem.o \
fksmb_idmap.o \
fksmb_init.o \
@@ -53,13 +54,17 @@ OBJS_FS_SMBSRV = \
smb_alloc.o \
smb_authenticate.o \
smb_close.o \
+ smb_cmn_rename.o \
+ smb_cmn_setfile.o \
smb_common_open.o \
smb_common_transact.o \
smb_create.o \
smb_delete.o \
+ smb_dfs.o \
smb_directory.o \
smb_dispatch.o \
smb_echo.o \
+ smb_errno.o \
smb_find.o \
smb_flush.o \
smb_fsinfo.o \
@@ -76,6 +81,7 @@ OBJS_FS_SMBSRV = \
smb_negotiate.o \
smb_net.o \
smb_node.o \
+ smb_notify.o \
smb_nt_cancel.o \
smb_nt_create_andx.o \
smb_nt_transact_create.o \
@@ -92,6 +98,7 @@ OBJS_FS_SMBSRV = \
smb_print.o \
smb_process_exit.o \
smb_query_fileinfo.o \
+ smb_quota.o \
smb_read.o \
smb_rename.o \
smb_sd.o \
@@ -112,7 +119,38 @@ OBJS_FS_SMBSRV = \
smb_vfs.o \
smb_vops.o \
smb_vss.o \
- smb_write.o
+ smb_write.o \
+ \
+ smb2_dispatch.o \
+ smb2_cancel.o \
+ smb2_change_notify.o \
+ smb2_close.o \
+ smb2_create.o \
+ smb2_echo.o \
+ smb2_flush.o \
+ smb2_ioctl.o \
+ smb2_lock.o \
+ smb2_logoff.o \
+ smb2_negotiate.o \
+ smb2_ofile.o \
+ smb2_oplock.o \
+ smb2_qinfo_file.o \
+ smb2_qinfo_fs.o \
+ smb2_qinfo_sec.o \
+ smb2_qinfo_quota.o \
+ smb2_query_dir.o \
+ smb2_query_info.o \
+ smb2_read.o \
+ smb2_session_setup.o \
+ smb2_set_info.o \
+ smb2_setinfo_file.o \
+ smb2_setinfo_fs.o \
+ smb2_setinfo_quota.o \
+ smb2_setinfo_sec.o \
+ smb2_signing.o \
+ smb2_tree_connect.o \
+ smb2_tree_disconn.o \
+ smb2_write.o
# Can't just link with -lsmb because of user vs kernel API
# i.e. can't call free with mem from kmem_alloc, which is
@@ -135,6 +173,7 @@ OBJS_MISC = \
acl_common.o \
pathname.o \
refstr.o \
+ smb_status2winerr.o \
xattr_common.o
OBJECTS = \
@@ -190,6 +229,10 @@ pics/acl_common.o: $(SRC)/common/acl/acl_common.c
$(COMPILE.c) -o $@ $(SRC)/common/acl/acl_common.c
$(POST_PROCESS_O)
+pics/smb_status2winerr.o: $(SRC)/common/smbclnt/smb_status2winerr.c
+ $(COMPILE.c) -o $@ $(SRC)/common/smbclnt/smb_status2winerr.c
+ $(POST_PROCESS_O)
+
pics/pathname.o: $(SRC)/uts/common/fs/pathname.c
$(COMPILE.c) -o $@ $(SRC)/uts/common/fs/pathname.c
$(POST_PROCESS_O)
diff --git a/usr/src/lib/smbsrv/libfksmbsrv/common/fake_vop.c b/usr/src/lib/smbsrv/libfksmbsrv/common/fake_vop.c
index 5f14cfc896..41c301fbaa 100644
--- a/usr/src/lib/smbsrv/libfksmbsrv/common/fake_vop.c
+++ b/usr/src/lib/smbsrv/libfksmbsrv/common/fake_vop.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -261,7 +261,7 @@ fop_setattr(
cred_t *cr,
caller_context_t *ct)
{
- struct timeval times[2];
+ timespec_t times[2];
if (vap->va_mask & AT_SIZE) {
if (ftruncate(vp->v_fd, vap->va_size) == -1)
@@ -274,20 +274,20 @@ fop_setattr(
(void) fop__setxvattr(vp, (xvattr_t *)vap);
if (vap->va_mask & (AT_ATIME | AT_MTIME)) {
- times[0].tv_sec = 0;
- times[0].tv_usec = UTIME_OMIT;
- times[1].tv_sec = 0;
- times[1].tv_usec = UTIME_OMIT;
if (vap->va_mask & AT_ATIME) {
- times[0].tv_sec = vap->va_atime.tv_sec;
- times[0].tv_usec = vap->va_atime.tv_nsec / 1000;
+ times[0] = vap->va_atime;
+ } else {
+ times[0].tv_sec = 0;
+ times[0].tv_nsec = UTIME_OMIT;
}
if (vap->va_mask & AT_MTIME) {
- times[1].tv_sec = vap->va_mtime.tv_sec;
- times[1].tv_usec = vap->va_mtime.tv_nsec / 1000;
+ times[1] = vap->va_mtime;
+ } else {
+ times[1].tv_sec = 0;
+ times[1].tv_nsec = UTIME_OMIT;
}
- (void) futimesat(vp->v_fd, NULL, times);
+ (void) futimens(vp->v_fd, times);
}
return (0);
@@ -449,11 +449,9 @@ fop_create(
}
/*
- * Truncate (if requested).
+ * Might need to set attributes.
*/
- if ((vap->va_mask & AT_SIZE) && vap->va_size == 0) {
- (void) ftruncate(vp->v_fd, 0);
- }
+ (void) fop_setattr(vp, vap, 0, cr, ct);
*vpp = vp;
return (0);
@@ -563,6 +561,11 @@ fop_mkdir(
*vpp = vncache_enter(&st, dvp, name, fd);
+ /*
+ * Might need to set attributes.
+ */
+ (void) fop_setattr(*vpp, vap, 0, cr, ct);
+
return (0);
}
diff --git a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_dt.c b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_dt.c
new file mode 100644
index 0000000000..e9f03d4c06
--- /dev/null
+++ b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_dt.c
@@ -0,0 +1,58 @@
+/*
+ * 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 2015 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#include <smbsrv/smb_kproto.h>
+
+/*
+ * See: DTRACE_PROBE... in smb_kproto.h
+ */
+
+int fksmbd_dtrace_log = 0;
+
+void
+smb_dtrace1(const char *f, const char *n,
+ const char *t1, long v1)
+{
+ if (fksmbd_dtrace_log) {
+ cmn_err(CE_CONT, "dtrace1:%s:%s,"
+ " (%s) 0x%lx\n",
+ f, n, t1, v1);
+ }
+}
+
+void
+smb_dtrace2(const char *f, const char *n,
+ const char *t1, long v1,
+ const char *t2, long v2)
+{
+ if (fksmbd_dtrace_log) {
+ cmn_err(CE_CONT, "dtrace2:%s:%s,"
+ " (%s) 0x%lx, (%s) 0x%lx\n",
+ f, n, t1, v1, t2, v2);
+ }
+}
+
+void
+smb_dtrace3(const char *f, const char *n,
+ const char *t1, long v1,
+ const char *t2, long v2,
+ const char *t3, long v3)
+{
+ if (fksmbd_dtrace_log) {
+ cmn_err(CE_CONT, "dtrace3:%s:%s,"
+ " (%s) 0x%lx, (%s) 0x%lx, (%s) 0x%lx\n",
+ f, n, t1, v1, t2, v2, t3, v3);
+ }
+}
diff --git a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_init.c b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_init.c
index 39ab56a1dd..f3e7463cb7 100644
--- a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_init.c
+++ b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_init.c
@@ -141,7 +141,7 @@ fksmbsrv_drv_close(void)
rc = smb_server_delete();
if (g_init_done != 0) {
- (void) smb_server_g_fini();
+ smb_server_g_fini();
g_init_done = 0;
}
diff --git a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_sign_pkcs.c b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_sign_pkcs.c
index 91596e5d67..ebafd8cd5a 100644
--- a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_sign_pkcs.c
+++ b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_sign_pkcs.c
@@ -14,7 +14,7 @@
*/
/*
- * Helper functions for SMB1 signing using PKCS#11
+ * Helper functions for SMB signing using PKCS#11
*
* There are two implementations of these functions:
* This one (for user space) and another for kernel.
@@ -87,3 +87,77 @@ smb_md5_final(smb_sign_ctx_t ctx, uint8_t *digest16)
return (rv == CKR_OK ? 0 : -1);
}
+
+/*
+ * SMB2 signing helpers:
+ * (getmech, init, update, final)
+ */
+
+int
+smb2_hmac_getmech(smb_sign_mech_t *mech)
+{
+ mech->mechanism = CKM_SHA256_HMAC;
+ mech->pParameter = NULL;
+ mech->ulParameterLen = 0;
+ return (0);
+}
+
+/*
+ * Start PKCS#11 session, load the key.
+ */
+int
+smb2_hmac_init(smb_sign_ctx_t *ctxp, smb_sign_mech_t *mech,
+ uint8_t *key, size_t key_len)
+{
+ CK_OBJECT_HANDLE hkey = 0;
+ CK_RV rv;
+
+ rv = SUNW_C_GetMechSession(mech->mechanism, ctxp);
+ if (rv != CKR_OK)
+ return (-1);
+
+ rv = SUNW_C_KeyToObject(*ctxp, mech->mechanism,
+ key, key_len, &hkey);
+ if (rv != CKR_OK)
+ return (-1);
+
+ rv = C_SignInit(*ctxp, mech, hkey);
+ (void) C_DestroyObject(*ctxp, hkey);
+
+ return (rv == CKR_OK ? 0 : -1);
+}
+
+/*
+ * Digest one segment
+ */
+int
+smb2_hmac_update(smb_sign_ctx_t ctx, uint8_t *in, size_t len)
+{
+ CK_RV rv;
+
+ rv = C_SignUpdate(ctx, in, len);
+ if (rv != CKR_OK)
+ (void) C_CloseSession(ctx);
+
+ return (rv == CKR_OK ? 0 : -1);
+}
+
+/*
+ * Note, the SMB2 signature is the first 16 bytes of the
+ * 32-byte SHA256 HMAC digest.
+ */
+int
+smb2_hmac_final(smb_sign_ctx_t ctx, uint8_t *digest16)
+{
+ uint8_t full_digest[SHA256_DIGEST_LENGTH];
+ CK_ULONG len = SHA256_DIGEST_LENGTH;
+ CK_RV rv;
+
+ rv = C_SignFinal(ctx, full_digest, &len);
+ if (rv == CKR_OK)
+ bcopy(full_digest, digest16, 16);
+
+ (void) C_CloseSession(ctx);
+
+ return (rv == CKR_OK ? 0 : -1);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smb_quota.c b/usr/src/lib/smbsrv/libmlsvc/common/smb_quota.c
index 2fdffde894..ceebb9d042 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/smb_quota.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_quota.c
@@ -1173,8 +1173,9 @@ smb_quota_add_ctrldir(const char *path)
if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) == 0) {
if ((nvlist_add_boolean_value(
attr, A_HIDDEN, 1) != 0) ||
- (nvlist_add_boolean_value(attr, A_SYSTEM, 1) != 0)
- || (fsetattr(dirfd, XATTR_VIEW_READWRITE, attr))) {
+ (nvlist_add_boolean_value(
+ attr, A_SYSTEM, 1) != 0) ||
+ (fsetattr(dirfd, XATTR_VIEW_READWRITE, attr))) {
nvlist_free(attr);
(void) close(dirfd);
if (qdir_created)
@@ -1222,6 +1223,7 @@ smb_quota_add_ctrldir(const char *path)
return;
}
+ aclp = NULL;
if (strcmp(acl_text, SMB_QUOTA_CNTRL_PERM) != 0) {
if (acl_fromtext(SMB_QUOTA_CNTRL_PERM, &aclp) != 0) {
free(acl_text);
@@ -1238,9 +1240,9 @@ smb_quota_add_ctrldir(const char *path)
acl_free(aclp);
return;
}
+ acl_free(aclp);
}
free(acl_text);
- acl_free(aclp);
}
/*
diff --git a/usr/src/lib/smbsrv/libsmb/common/libsmb.h b/usr/src/lib/smbsrv/libsmb/common/libsmb.h
index 6932e5d3a4..fdda80dc2a 100644
--- a/usr/src/lib/smbsrv/libsmb/common/libsmb.h
+++ b/usr/src/lib/smbsrv/libsmb/common/libsmb.h
@@ -151,6 +151,10 @@ typedef enum {
SMB_CI_DISPOSITION,
SMB_CI_DFS_STDROOT_NUM,
SMB_CI_TRAVERSE_MOUNTS,
+ SMB_CI_SMB2_ENABLE_OLD, /* obsolete */
+ SMB_CI_INITIAL_CREDITS,
+ SMB_CI_MAXIMUM_CREDITS,
+ SMB_CI_MAX_PROTOCOL,
SMB_CI_MAX
} smb_cfg_id_t;
@@ -172,6 +176,7 @@ extern int smb_smf_set_opaque_property(smb_scfhandle_t *, char *,
extern int smb_smf_get_opaque_property(smb_scfhandle_t *, char *,
void *, size_t);
extern int smb_smf_create_service_pgroup(smb_scfhandle_t *, char *);
+extern int smb_smf_delete_property(smb_scfhandle_t *, char *);
extern int smb_smf_restart_service(void);
extern int smb_smf_maintenance_mode(void);
@@ -206,6 +211,10 @@ extern void smb_config_get_version(smb_version_t *);
uint32_t smb_config_get_execinfo(char *, char *, size_t);
extern void smb_config_get_negtok(uchar_t *, uint32_t *);
+extern int smb_config_check_protocol(char *);
+extern uint32_t smb_config_get_max_protocol(void);
+extern void smb_config_upgrade(void);
+
extern void smb_load_kconfig(smb_kmod_cfg_t *kcfg);
extern uint32_t smb_crc_gen(uint8_t *, size_t);
diff --git a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
index 5c88aa1d76..2af8c7579b 100644
--- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
+++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
@@ -19,7 +19,7 @@
#
#
# Copyright (c) 2007, 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.
#
#
@@ -97,6 +97,7 @@ SYMBOL_VERSION SUNWprivate {
smb_codepage_init;
smb_common_decode;
smb_common_encode;
+ smb_config_check_protocol;
smb_config_get;
smb_config_get_ads_enable;
smb_config_get_debug;
@@ -121,6 +122,7 @@ SYMBOL_VERSION SUNWprivate {
smb_config_setdomaininfo;
smb_config_setnum;
smb_config_setstr;
+ smb_config_upgrade;
smb_crc_gen;
smb_ctxbuf_init;
smb_ctxbuf_len;
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c
index 83a7cae7bc..a82e55f5ff 100644
--- a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.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 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -49,6 +49,11 @@ typedef struct smb_cfg_param {
uint32_t sc_flags;
} smb_cfg_param_t;
+struct str_val {
+ char *str;
+ uint32_t val;
+};
+
/*
* config parameter flags
*/
@@ -136,6 +141,10 @@ static smb_cfg_param_t smb_cfg_table[] =
{SMB_CI_DISPOSITION, "disposition", SCF_TYPE_ASTRING, SMB_CF_EXEC},
{SMB_CI_DFS_STDROOT_NUM, "dfs_stdroot_num", SCF_TYPE_INTEGER, 0},
{SMB_CI_TRAVERSE_MOUNTS, "traverse_mounts", SCF_TYPE_BOOLEAN, 0},
+ {SMB_CI_SMB2_ENABLE_OLD, "smb2_enable", SCF_TYPE_BOOLEAN, 0},
+ {SMB_CI_INITIAL_CREDITS, "initial_credits", SCF_TYPE_INTEGER, 0},
+ {SMB_CI_MAXIMUM_CREDITS, "maximum_credits", SCF_TYPE_INTEGER, 0},
+ {SMB_CI_MAX_PROTOCOL, "max_protocol", SCF_TYPE_ASTRING, 0},
/* SMB_CI_MAX */
};
@@ -1077,3 +1086,165 @@ smb_config_getent(smb_cfg_id_t id)
assert(0);
return (NULL);
}
+
+
+/*
+ * We store the max SMB protocol version in SMF as a string,
+ * (for convenience of svccfg etc) but the programmatic get/set
+ * interfaces use the numeric form.
+ *
+ * The numeric values are as defined in the [MS-SMB2] spec.
+ * except for how we represent "1" (for SMB1) which is an
+ * arbitrary value below SMB2_VERS_BASE.
+ */
+static struct str_val
+smb_versions[] = {
+ { "3.0", SMB_VERS_3_0 },
+ { "2.1", SMB_VERS_2_1 },
+ { "2.002", SMB_VERS_2_002 },
+ { "1", SMB_VERS_1 },
+ { NULL, 0 }
+};
+
+/*
+ * This really should be the latest (SMB_VERS_3_0)
+ * but we're being cautious with SMB3 for a while.
+ */
+uint32_t max_protocol_default = SMB_VERS_2_1;
+
+uint32_t
+smb_config_get_max_protocol(void)
+{
+ char str[SMB_VERSTR_LEN];
+ int i, rc;
+
+ rc = smb_config_getstr(SMB_CI_MAX_PROTOCOL, str, sizeof (str));
+ if (rc == SMBD_SMF_OK) {
+ for (i = 0; smb_versions[i].str != NULL; i++) {
+ if (strcmp(str, smb_versions[i].str) == 0)
+ return (smb_versions[i].val);
+ }
+ if (str[0] != '\0') {
+ syslog(LOG_ERR, "smbd/max_protocol value invalid");
+ }
+ }
+
+ return (max_protocol_default);
+}
+
+int
+smb_config_check_protocol(char *value)
+{
+ int i;
+
+ for (i = 0; smb_versions[i].str != NULL; i++) {
+ if (strcmp(value, smb_versions[i].str) == 0)
+ return (0);
+ }
+
+ return (-1);
+}
+
+/*
+ * If smb2_enable is present and max_protocol is empty,
+ * set max_protocol. Delete smb2_enable.
+ */
+static void
+upgrade_smb2_enable()
+{
+ smb_scfhandle_t *handle;
+ char *s2e_name = "smb2_enable";
+ char *s2e_sval;
+ uint8_t s2e_bval;
+ char *maxp_name = "max_protocol";
+ char *maxp_sval;
+ char verstr[SMB_VERSTR_LEN];
+ int rc;
+
+ handle = smb_smf_scf_init(SMBD_FMRI_PREFIX);
+ if (handle == NULL)
+ return;
+ rc = smb_smf_create_service_pgroup(handle, SMBD_PG_NAME);
+ if (rc != SMBD_SMF_OK)
+ goto out;
+
+ /* Is there an "smb2_enable" property? */
+ rc = smb_smf_get_boolean_property(handle, s2e_name, &s2e_bval);
+ if (rc != SMBD_SMF_OK) {
+ syslog(LOG_DEBUG, "upgrade: smb2_enable not found");
+ goto out;
+ }
+
+ /*
+ * We will try to delete the smb2_enable property, so we need
+ * the transaction to start now, before we modify max_protocol
+ */
+ if ((rc = smb_smf_start_transaction(handle)) != 0) {
+ syslog(LOG_DEBUG, "upgrade_smb2_enable: start trans (%d)", rc);
+ goto out;
+ }
+
+ /*
+ * Old (smb2_enable) property exists.
+ * Does the new one? (max_protocol)
+ */
+ rc = smb_smf_get_string_property(handle, maxp_name,
+ verstr, sizeof (verstr));
+ if (rc == SMBD_SMF_OK && !smb_config_check_protocol(verstr)) {
+ syslog(LOG_DEBUG, "upgrade: found %s = %s",
+ maxp_name, verstr);
+ /* Leave existing max_protocol as we found it. */
+ } else {
+ /*
+ * New property missing or invalid.
+ * Upgrade from "smb2_enable".
+ */
+ if (s2e_bval == 0) {
+ s2e_sval = "false";
+ maxp_sval = "1";
+ } else {
+ s2e_sval = "true";
+ maxp_sval = "2.1";
+ }
+ /*
+ * Note: Need this in the same transaction as the
+ * delete of smb2_enable below.
+ */
+ rc = smb_smf_set_string_property(handle, maxp_name, maxp_sval);
+ if (rc != SMBD_SMF_OK) {
+ syslog(LOG_ERR, "failed to set smbd/%d (%d)",
+ maxp_name, rc);
+ goto out;
+ }
+ syslog(LOG_INFO, "upgrade smbd/smb2_enable=%s "
+ "converted to smbd/max_protocol=%s",
+ s2e_sval, maxp_sval);
+ }
+
+ /*
+ * Delete the old smb2_enable property.
+ */
+ if ((rc = smb_smf_delete_property(handle, s2e_name)) != 0) {
+ syslog(LOG_DEBUG, "upgrade_smb2_enable: delete prop (%d)", rc);
+ } else if ((rc = smb_smf_end_transaction(handle)) != 0) {
+ syslog(LOG_DEBUG, "upgrade_smb2_enable: end trans (%d)", rc);
+ }
+ if (rc != 0) {
+ syslog(LOG_ERR, "failed to delete property smbd/%d (%d)",
+ s2e_name, rc);
+ }
+
+out:
+ (void) smb_smf_end_transaction(handle);
+ smb_smf_scf_fini(handle);
+}
+
+
+/*
+ * Run once at startup convert old SMF settings to current.
+ */
+void
+smb_config_upgrade(void)
+{
+ upgrade_smb2_enable();
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_info.c b/usr/src/lib/smbsrv/libsmb/common/smb_info.c
index 0dcae43179..e3e2ab15d9 100644
--- a/usr/src/lib/smbsrv/libsmb/common/smb_info.c
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_info.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 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -63,24 +63,81 @@ static smb_ipc_t ipc_orig_info;
static rwlock_t smb_ipc_lock;
/*
- * Some older clients (Windows 98) only handle the low byte
- * of the max workers value. If the low byte is less than
- * SMB_PI_MAX_WORKERS_MIN set it to SMB_PI_MAX_WORKERS_MIN.
+ * These three parameters are all related:
+ * skc_initial_credits
+ * skc_maximum_credits
+ * skc_maxworkers (max worker threads)
+ * They must be in non-decreasing order. Get the values in order:
+ * maxworkers, maximum_credits, initial_credits
+ * enforcing maximum values and relations as we go. Then in the
+ * opposite order check minimum values and relations.
+ *
+ * smb_config_getnum puts a zero in the &citem if it fails getting
+ * the parameter value. When fetch parameters for which zero is OK,
+ * the return code is intentionally ignored.
*/
void
smb_load_kconfig(smb_kmod_cfg_t *kcfg)
{
struct utsname uts;
int64_t citem;
+ int rc;
bzero(kcfg, sizeof (smb_kmod_cfg_t));
- (void) smb_config_getnum(SMB_CI_MAX_WORKERS, &citem);
+ /*
+ * skc_maxworkers (max. no. of taskq worker threads)
+ */
+ rc = smb_config_getnum(SMB_CI_MAX_WORKERS, &citem);
+ if (rc != SMBD_SMF_OK)
+ citem = SMB_PI_MAX_WORKERS_DEF;
+ if (citem > SMB_PI_MAX_WORKERS_MAX)
+ citem = SMB_PI_MAX_WORKERS_MAX;
kcfg->skc_maxworkers = (uint32_t)citem;
- if ((kcfg->skc_maxworkers & 0xFF) < SMB_PI_MAX_WORKERS_MIN) {
- kcfg->skc_maxworkers &= ~0xFF;
- kcfg->skc_maxworkers += SMB_PI_MAX_WORKERS_MIN;
- }
+
+ /*
+ * The largest number of credits we let a single client have.
+ * It never makes sense for this to be > max_workers
+ */
+ rc = smb_config_getnum(SMB_CI_MAXIMUM_CREDITS, &citem);
+ if (rc != SMBD_SMF_OK)
+ citem = SMB_PI_MAXIMUM_CREDITS_DEF;
+ if (citem > SMB_PI_MAXIMUM_CREDITS_MAX)
+ citem = SMB_PI_MAXIMUM_CREDITS_MAX;
+ kcfg->skc_maximum_credits = (uint16_t)citem;
+ if (kcfg->skc_maximum_credits > kcfg->skc_maxworkers)
+ kcfg->skc_maximum_credits = (uint16_t)kcfg->skc_maxworkers;
+
+ /*
+ * The number of credits we give a client initially.
+ * Should be enough for a "light" workload, as the
+ * client will request additional credits when the
+ * workload increases. Must be <= maximum_credits.
+ */
+ rc = smb_config_getnum(SMB_CI_INITIAL_CREDITS, &citem);
+ if (rc != SMBD_SMF_OK)
+ citem = SMB_PI_INITIAL_CREDITS_DEF;
+ if (citem > SMB_PI_INITIAL_CREDITS_MAX)
+ citem = SMB_PI_INITIAL_CREDITS_MAX;
+ kcfg->skc_initial_credits = (uint16_t)citem;
+ if (kcfg->skc_initial_credits > kcfg->skc_maximum_credits)
+ kcfg->skc_initial_credits = kcfg->skc_maximum_credits;
+
+ /*
+ * Now enforce minimums, smaller to larger.
+ */
+ if (kcfg->skc_initial_credits < SMB_PI_INITIAL_CREDITS_MIN)
+ kcfg->skc_initial_credits = SMB_PI_INITIAL_CREDITS_MIN;
+
+ if (kcfg->skc_maximum_credits < SMB_PI_MAXIMUM_CREDITS_MIN)
+ kcfg->skc_maximum_credits = SMB_PI_MAXIMUM_CREDITS_MIN;
+ if (kcfg->skc_maximum_credits < kcfg->skc_initial_credits)
+ kcfg->skc_maximum_credits = kcfg->skc_initial_credits;
+
+ if (kcfg->skc_maxworkers < SMB_PI_MAX_WORKERS_MIN)
+ kcfg->skc_maxworkers = SMB_PI_MAX_WORKERS_MIN;
+ if (kcfg->skc_maxworkers < kcfg->skc_maximum_credits)
+ kcfg->skc_maxworkers = kcfg->skc_maximum_credits;
(void) smb_config_getnum(SMB_CI_KEEPALIVE, &citem);
kcfg->skc_keepalive = (uint32_t)citem;
@@ -99,7 +156,9 @@ smb_load_kconfig(smb_kmod_cfg_t *kcfg)
kcfg->skc_oplock_enable = smb_config_getbool(SMB_CI_OPLOCK_ENABLE);
kcfg->skc_sync_enable = smb_config_getbool(SMB_CI_SYNC_ENABLE);
kcfg->skc_traverse_mounts = smb_config_getbool(SMB_CI_TRAVERSE_MOUNTS);
+ kcfg->skc_max_protocol = smb_config_get_max_protocol();
kcfg->skc_secmode = smb_config_get_secmode();
+
(void) smb_getdomainname(kcfg->skc_nbdomain,
sizeof (kcfg->skc_nbdomain));
(void) smb_getfqdomainname(kcfg->skc_fqdn,
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_kmod.c b/usr/src/lib/smbsrv/libsmb/common/smb_kmod.c
index 8b7b32fb19..8c330eeb33 100644
--- a/usr/src/lib/smbsrv/libsmb/common/smb_kmod.c
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_kmod.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -88,9 +88,12 @@ smb_kmod_setcfg(smb_kmod_cfg_t *cfg)
ioc.ipv6_enable = cfg->skc_ipv6_enable;
ioc.print_enable = cfg->skc_print_enable;
ioc.traverse_mounts = cfg->skc_traverse_mounts;
+ ioc.max_protocol = cfg->skc_max_protocol;
ioc.exec_flags = cfg->skc_execflags;
ioc.negtok_len = cfg->skc_negtok_len;
ioc.version = cfg->skc_version;
+ ioc.initial_credits = cfg->skc_initial_credits;
+ ioc.maximum_credits = cfg->skc_maximum_credits;
(void) memcpy(ioc.machine_uuid, cfg->skc_machine_uuid, sizeof (uuid_t));
(void) memcpy(ioc.negtok, cfg->skc_negtok, sizeof (ioc.negtok));
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c b/usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c
index ac35bbd7f7..2252f7ab3d 100644
--- a/usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c
@@ -22,7 +22,7 @@
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/* helper functions for using libscf with CIFS */
@@ -155,6 +155,7 @@ int
smb_smf_end_transaction(smb_scfhandle_t *handle)
{
int ret = SMBD_SMF_OK;
+ int rc;
if (handle == NULL)
return (SMBD_SMF_SYSTEM_ERR);
@@ -162,9 +163,16 @@ smb_smf_end_transaction(smb_scfhandle_t *handle)
if (handle->scf_trans == NULL) {
ret = SMBD_SMF_SYSTEM_ERR;
} else {
- if (scf_transaction_commit(handle->scf_trans) < 0) {
+ rc = scf_transaction_commit(handle->scf_trans);
+ if (rc == 1) {
+ ret = SMBD_SMF_OK;
+ } else if (rc == 0) {
+ ret = SMBD_SMF_INVALID_ARG;
+ smb_smf_scf_log_error("Failed to commit, old pg: "
+ "transaction: %s");
+ } else {
ret = SMBD_SMF_SYSTEM_ERR;
- smb_smf_scf_log_error("Failed to commit "
+ smb_smf_scf_log_error("Failed to commit, error: "
"transaction: %s");
}
scf_transaction_destroy_children(handle->scf_trans);
@@ -562,6 +570,54 @@ smb_smf_get_opaque_property(smb_scfhandle_t *handle, char *propname,
}
/*
+ * Delete a property (for properties obsoleted during an upgrade).
+ */
+int
+smb_smf_delete_property(smb_scfhandle_t *handle, char *propname)
+{
+ scf_transaction_entry_t *entry;
+ int ret = SMBD_SMF_OK;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+ if (handle->scf_trans == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ /*
+ * properties must be set in transactions and don't take
+ * effect until the transaction has been ended/committed.
+ */
+ entry = scf_entry_create(handle->scf_handle);
+ if (entry == NULL) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ goto out;
+ }
+
+ if (scf_transaction_property_delete(handle->scf_trans,
+ entry, propname) == 0) {
+ /* the entry is in the transaction */
+ entry = NULL;
+ } else {
+ switch (scf_error()) {
+ case SCF_ERROR_NOT_FOUND:
+ /* Did not exist. We're done. */
+ ret = SMBD_SMF_OK;
+ goto out;
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SMBD_SMF_NO_PERMISSION;
+ goto out;
+ default:
+ ret = SMBD_SMF_SYSTEM_ERR;
+ goto out;
+ }
+ }
+
+out:
+ scf_entry_destroy(entry);
+ return (ret);
+}
+
+/*
* Put the smb service into maintenance mode.
*/
int
diff --git a/usr/src/man/man4/smb.4 b/usr/src/man/man4/smb.4
index 1bce91817e..b7073a0f65 100644
--- a/usr/src/man/man4/smb.4
+++ b/usr/src/man/man4/smb.4
@@ -8,7 +8,6 @@
.SH NAME
smb \- configuration properties for Solaris CIFS server
.SH DESCRIPTION
-.sp
.LP
Behavior of the Solaris CIFS server is defined by property values that are
stored in the Service Management Facility, \fBsmf\fR(5).
@@ -305,6 +304,18 @@ The UID of the Unix user.
.sp
.ne 2
.na
+\fB\fBmax_protocol\fR\fR
+.ad
+.sp .6
+.RS 4n
+Specifies the maximum SMB protocol level that the SMB service
+should allow clients to negotiate. The default value is \fB2.1\fR.
+Valid settings include: \fB1\fR, \fB2.1\fR, \fB3.0\fR
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBmax_workers\fR\fR
.ad
.sp .6
@@ -459,7 +470,6 @@ set.
.RE
.SH ATTRIBUTES
-.sp
.LP
See the \fBattributes\fR(5) man page for descriptions of the following
attributes:
@@ -476,7 +486,6 @@ Interface Stability Uncommitted
.TE
.SH SEE ALSO
-.sp
.LP
\fBsharectl\fR(1M), \fBsmbadm\fR(1M), \fBsmbd\fR(1M), \fBsmbstat\fR(1M),
\fBattributes\fR(5), \fBsmf\fR(5)
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index c90a5c1773..7eef5c5302 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -1165,33 +1165,39 @@ NFSSRV_OBJS += nfs_server.o nfs_srv.o nfs3_srv.o \
nfs4_deleg_ops.o nfs4_srv_readdir.o nfs4_dispatch.o
SMBSRV_SHARED_OBJS += \
+ smb_door_legacy.o \
smb_inet.o \
smb_match.o \
smb_msgbuf.o \
+ smb_native.o \
+ smb_netbios_util.o \
smb_oem.o \
+ smb_sid.o \
+ smb_status2winerr.o \
smb_string.o \
- smb_utf8.o \
- smb_door_legacy.o \
- smb_xdr.o \
smb_token.o \
smb_token_xdr.o \
- smb_sid.o \
- smb_native.o \
- smb_netbios_util.o
+ smb_utf8.o \
+ smb_xdr.o
+# See also: $SRC/lib/smbsrv/libfksmbsrv/Makefile.com
SMBSRV_OBJS += $(SMBSRV_SHARED_OBJS) \
smb_acl.o \
smb_alloc.o \
smb_authenticate.o \
smb_close.o \
+ smb_cmn_rename.o \
+ smb_cmn_setfile.o \
smb_common_open.o \
smb_common_transact.o \
smb_create.o \
smb_cred.o \
smb_delete.o \
+ smb_dfs.o \
smb_directory.o \
smb_dispatch.o \
smb_echo.o \
+ smb_errno.o \
smb_fem.o \
smb_find.o \
smb_flush.o \
@@ -1212,6 +1218,7 @@ SMBSRV_OBJS += $(SMBSRV_SHARED_OBJS) \
smb_negotiate.o \
smb_net.o \
smb_node.o \
+ smb_notify.o \
smb_nt_cancel.o \
smb_nt_create_andx.o \
smb_nt_transact_create.o \
@@ -1228,6 +1235,7 @@ SMBSRV_OBJS += $(SMBSRV_SHARED_OBJS) \
smb_print.o \
smb_process_exit.o \
smb_query_fileinfo.o \
+ smb_quota.o \
smb_read.o \
smb_rename.o \
smb_sd.o \
@@ -1249,7 +1257,38 @@ SMBSRV_OBJS += $(SMBSRV_SHARED_OBJS) \
smb_vfs.o \
smb_vops.o \
smb_vss.o \
- smb_write.o
+ smb_write.o \
+ \
+ smb2_dispatch.o \
+ smb2_cancel.o \
+ smb2_change_notify.o \
+ smb2_close.o \
+ smb2_create.o \
+ smb2_echo.o \
+ smb2_flush.o \
+ smb2_ioctl.o \
+ smb2_lock.o \
+ smb2_logoff.o \
+ smb2_negotiate.o \
+ smb2_ofile.o \
+ smb2_oplock.o \
+ smb2_qinfo_file.o \
+ smb2_qinfo_fs.o \
+ smb2_qinfo_sec.o \
+ smb2_qinfo_quota.o \
+ smb2_query_dir.o \
+ smb2_query_info.o \
+ smb2_read.o \
+ smb2_session_setup.o \
+ smb2_set_info.o \
+ smb2_setinfo_file.o \
+ smb2_setinfo_fs.o \
+ smb2_setinfo_quota.o \
+ smb2_setinfo_sec.o \
+ smb2_signing.o \
+ smb2_tree_connect.o \
+ smb2_tree_disconn.o \
+ smb2_write.o
PCFS_OBJS += pc_alloc.o pc_dir.o pc_node.o pc_subr.o \
pc_vfsops.o pc_vnops.o
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_cancel.c b/usr/src/uts/common/fs/smbsrv/smb2_cancel.c
new file mode 100644
index 0000000000..d8ed9e0ac7
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_cancel.c
@@ -0,0 +1,98 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_CANCEL
+ */
+
+#include <smbsrv/smb2_kproto.h>
+
+static void smb2sr_cancel_async(smb_request_t *);
+static void smb2sr_cancel_sync(smb_request_t *);
+
+/*
+ * This handles an SMB2_CANCEL request when seen in the reader.
+ * (See smb2sr_newrq) Handle this immediately, rather than
+ * going through the normal taskq dispatch mechanism.
+ * Note that Cancel does NOT get a response.
+ */
+int
+smb2sr_newrq_cancel(smb_request_t *sr)
+{
+ int rc;
+
+ /*
+ * Decode the header
+ */
+ if ((rc = smb2_decode_header(sr)) != 0)
+ return (rc);
+
+ if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND)
+ smb2sr_cancel_async(sr);
+ else
+ smb2sr_cancel_sync(sr);
+
+ return (0);
+}
+
+static void
+smb2sr_cancel_sync(smb_request_t *sr)
+{
+ struct smb_request *req;
+ struct smb_session *session = sr->session;
+ int cnt = 0;
+
+ smb_slist_enter(&session->s_req_list);
+ req = smb_slist_head(&session->s_req_list);
+ while (req) {
+ ASSERT(req->sr_magic == SMB_REQ_MAGIC);
+ if ((req != sr) &&
+ (req->smb2_messageid == sr->smb2_messageid)) {
+ smb_request_cancel(req);
+ cnt++;
+ }
+ req = smb_slist_next(&session->s_req_list, req);
+ }
+ if (cnt != 1) {
+ DTRACE_PROBE2(smb2__cancel__error,
+ uint64_t, sr->smb2_messageid, int, cnt);
+ }
+ smb_slist_exit(&session->s_req_list);
+}
+
+static void
+smb2sr_cancel_async(smb_request_t *sr)
+{
+ struct smb_request *req;
+ struct smb_session *session = sr->session;
+ int cnt = 0;
+
+ smb_slist_enter(&session->s_req_list);
+ req = smb_slist_head(&session->s_req_list);
+ while (req) {
+ ASSERT(req->sr_magic == SMB_REQ_MAGIC);
+ if ((req != sr) &&
+ (req->smb2_async_id == sr->smb2_async_id)) {
+ smb_request_cancel(req);
+ cnt++;
+ }
+ req = smb_slist_next(&session->s_req_list, req);
+ }
+ if (cnt != 1) {
+ DTRACE_PROBE2(smb2__cancel__error,
+ uint64_t, sr->smb2_async_id, int, cnt);
+ }
+ smb_slist_exit(&session->s_req_list);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_change_notify.c b/usr/src/uts/common/fs/smbsrv/smb2_change_notify.c
new file mode 100644
index 0000000000..8823d7945c
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_change_notify.c
@@ -0,0 +1,147 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_CHANGE_NOTIFY
+ */
+
+#include <smbsrv/smb2_kproto.h>
+
+static smb_sdrc_t smb2_change_notify_async(smb_request_t *);
+
+smb_sdrc_t
+smb2_change_notify(smb_request_t *sr)
+{
+ smb_node_t *node = NULL;
+ uint16_t StructSize;
+ uint16_t iFlags;
+ uint32_t oBufLength;
+ smb2fid_t smb2fid;
+ uint32_t CompletionFilter;
+ uint32_t reserved;
+ uint32_t status;
+ int rc = 0;
+
+ /*
+ * SMB2 Change Notify request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "wwlqqll",
+ &StructSize, /* w */
+ &iFlags, /* w */
+ &oBufLength, /* l */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal, /* q */
+ &CompletionFilter, /* l */
+ &reserved); /* l */
+ if (rc || StructSize != 32)
+ return (SDRC_ERROR);
+
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status)
+ goto puterror;
+
+ node = sr->fid_ofile->f_node;
+ if (node == NULL || !smb_node_is_dir(node)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto puterror;
+ }
+
+ /*
+ * Let Change Notify "go async", because it
+ * may block indefinitely.
+ */
+ status = smb2sr_go_async(sr, smb2_change_notify_async);
+puterror:
+ ASSERT(status != 0);
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+}
+
+static smb_sdrc_t
+smb2_change_notify_async(smb_request_t *sr)
+{
+ uint16_t StructSize;
+ uint16_t iFlags;
+ uint32_t oBufLength;
+ smb2fid_t smb2fid;
+ uint32_t CompletionFilter;
+ uint32_t reserved;
+ uint32_t status;
+ uint16_t DataOff;
+ int rc = 0;
+
+ /*
+ * SMB2 Change Notify request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "wwlqqll",
+ &StructSize, /* w */
+ &iFlags, /* w */
+ &oBufLength, /* l */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal, /* q */
+ &CompletionFilter, /* l */
+ &reserved); /* l */
+ if (rc || StructSize != 32)
+ return (SDRC_ERROR);
+
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status != 0) {
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+ }
+
+ CompletionFilter &= FILE_NOTIFY_VALID_MASK;
+ if (iFlags & SMB2_WATCH_TREE)
+ CompletionFilter |= NODE_FLAGS_WATCH_TREE;
+
+ if (oBufLength > smb2_max_trans)
+ oBufLength = smb2_max_trans;
+ sr->raw_data.max_bytes = oBufLength;
+
+ status = smb_notify_common(sr, &sr->raw_data, CompletionFilter);
+ if (status != 0) {
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+ }
+
+ /*
+ * SMB2 Change Notify reply
+ */
+ DataOff = SMB2_HDR_SIZE + 8;
+ oBufLength = MBC_LENGTH(&sr->raw_data);
+ rc = smb_mbc_encodef(
+ &sr->reply, "wwlC",
+ 9, /* StructSize */ /* w */
+ DataOff, /* w */
+ oBufLength, /* l */
+ &sr->raw_data); /* C */
+ if (rc)
+ return (SDRC_ERROR);
+
+ return (SDRC_SUCCESS);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_close.c b/usr/src/uts/common/fs/smbsrv/smb2_close.c
new file mode 100644
index 0000000000..449b47cf9f
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_close.c
@@ -0,0 +1,90 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_CLOSE
+ */
+
+#include <smbsrv/smb2_kproto.h>
+
+smb_sdrc_t
+smb2_close(smb_request_t *sr)
+{
+ smb_attr_t attr;
+ smb_ofile_t *of;
+ uint16_t StructSize;
+ uint16_t Flags;
+ uint32_t reserved;
+ smb2fid_t smb2fid;
+ uint32_t status;
+ int rc = 0;
+
+ /*
+ * SMB2 Close request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "wwlqq",
+ &StructSize, /* w */
+ &Flags, /* w */
+ &reserved, /* l */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal); /* q */
+ if (rc)
+ return (SDRC_ERROR);
+ if (StructSize != 24)
+ return (SDRC_ERROR);
+
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status) {
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+ }
+ of = sr->fid_ofile;
+
+ bzero(&attr, sizeof (attr));
+ if (Flags & SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) {
+ attr.sa_mask = SMB_AT_ALL;
+ status = smb2_ofile_getattr(sr, of, &attr);
+ if (status) {
+ /*
+ * We could not stat the open file.
+ * Let's not fail the close call,
+ * but just turn off the flag.
+ */
+ Flags = 0;
+ }
+ }
+
+ smb_ofile_close(of, 0);
+
+ /*
+ * SMB2 Close reply
+ */
+ (void) smb_mbc_encodef(
+ &sr->reply,
+ "wwlTTTTqql",
+ 60, /* StructSize */ /* w */
+ Flags, /* w */
+ 0, /* reserved */ /* l */
+ &attr.sa_crtime, /* T */
+ &attr.sa_vattr.va_atime, /* T */
+ &attr.sa_vattr.va_mtime, /* T */
+ &attr.sa_vattr.va_ctime, /* T */
+ attr.sa_allocsz, /* q */
+ attr.sa_vattr.va_size, /* q */
+ attr.sa_dosattr); /* l */
+
+ return (SDRC_SUCCESS);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_create.c b/usr/src/uts/common/fs/smbsrv/smb2_create.c
new file mode 100644
index 0000000000..870ac656b4
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_create.c
@@ -0,0 +1,668 @@
+/*
+ * 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 2015 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_CREATE
+ * [MS-SMB2] 2.2.13
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+
+/*
+ * Some flags used locally to keep track of which Create Context
+ * names have been provided and/or requested.
+ */
+#define CCTX_EA_BUFFER 1
+#define CCTX_SD_BUFFER 2
+#define CCTX_DH_REQUEST 4
+#define CCTX_DH_RECONNECT 8
+#define CCTX_ALLOCATION_SIZE 0x10
+#define CCTX_QUERY_MAX_ACCESS 0x20
+#define CCTX_TIMEWARP_TOKEN 0x40
+#define CCTX_QUERY_ON_DISK_ID 0x80
+#define CCTX_REQUEST_LEASE 0x100
+
+
+typedef struct smb2_create_ctx_elem {
+ uint32_t cce_len;
+ mbuf_chain_t cce_mbc;
+} smb2_create_ctx_elem_t;
+
+typedef struct smb2_create_ctx {
+ uint_t cc_in_flags; /* CCTX_... */
+ uint_t cc_out_flags; /* CCTX_... */
+ /* Elements we may see in the request. */
+ smb2_create_ctx_elem_t cc_in_ext_attr;
+ smb2_create_ctx_elem_t cc_in_sec_desc;
+ smb2_create_ctx_elem_t cc_in_dh_request;
+ smb2_create_ctx_elem_t cc_in_dh_reconnect;
+ smb2_create_ctx_elem_t cc_in_alloc_size;
+ smb2_create_ctx_elem_t cc_in_time_warp;
+ smb2_create_ctx_elem_t cc_in_req_lease;
+ /* 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_t;
+
+static uint32_t smb2_decode_create_ctx(
+ mbuf_chain_t *, smb2_create_ctx_t *);
+static uint32_t smb2_encode_create_ctx(
+ mbuf_chain_t *, smb2_create_ctx_t *);
+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 *);
+
+smb_sdrc_t
+smb2_create(smb_request_t *sr)
+{
+ smb_attr_t *attr;
+ smb2_create_ctx_elem_t *cce;
+ smb2_create_ctx_t cctx;
+ mbuf_chain_t cc_mbc;
+ smb_arg_open_t *op = &sr->arg.open;
+ smb_ofile_t *of = NULL;
+ uint16_t StructSize;
+ uint8_t SecurityFlags;
+ uint8_t OplockLevel;
+ uint32_t ImpersonationLevel;
+ uint64_t SmbCreateFlags;
+ uint64_t Reserved4;
+ uint16_t NameOffset;
+ uint16_t NameLength;
+ uint32_t CreateCtxOffset;
+ uint32_t CreateCtxLength;
+ smb2fid_t smb2fid;
+ uint32_t status;
+ int skip;
+ int rc = 0;
+
+ bzero(&cctx, sizeof (cctx));
+ bzero(&cc_mbc, sizeof (cc_mbc));
+
+ /*
+ * Paranoia. This will set sr->fid_ofile, so
+ * if we already have one, release it now.
+ */
+ if (sr->fid_ofile != NULL) {
+ smb_ofile_request_complete(sr->fid_ofile);
+ smb_ofile_release(sr->fid_ofile);
+ sr->fid_ofile = NULL;
+ }
+
+ /*
+ * SMB2 Create request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "wbblqqlllllwwll",
+ &StructSize, /* w */
+ &SecurityFlags, /* b */
+ &OplockLevel, /* b */
+ &ImpersonationLevel, /* l */
+ &SmbCreateFlags, /* q */
+ &Reserved4, /* q */
+ &op->desired_access, /* l */
+ &op->dattr, /* l */
+ &op->share_access, /* l */
+ &op->create_disposition, /* l */
+ &op->create_options, /* l */
+ &NameOffset, /* w */
+ &NameLength, /* w */
+ &CreateCtxOffset, /* l */
+ &CreateCtxLength); /* l */
+ if (rc != 0 || StructSize != 57)
+ return (SDRC_ERROR);
+
+ /*
+ * We're normally positioned at the path name now,
+ * but there could be some padding before it.
+ */
+ skip = (NameOffset + sr->smb2_cmd_hdr) -
+ sr->smb_data.chain_offset;
+ if (skip < 0) {
+ status = NT_STATUS_OBJECT_PATH_INVALID;
+ goto errout;
+ }
+ if (skip > 0)
+ (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
+
+ /*
+ * Get the path name
+ */
+ if (NameLength >= SMB_MAXPATHLEN) {
+ status = NT_STATUS_OBJECT_PATH_INVALID;
+ goto errout;
+ }
+ if (NameLength == 0) {
+ op->fqi.fq_path.pn_path = "\\";
+ } else {
+ rc = smb_mbc_decodef(&sr->smb_data, "%#U", sr,
+ NameLength, &op->fqi.fq_path.pn_path);
+ if (rc) {
+ status = NT_STATUS_OBJECT_PATH_INVALID;
+ goto errout;
+ }
+ }
+ op->fqi.fq_dnode = sr->tid_tree->t_snode;
+
+ switch (OplockLevel) {
+ case SMB2_OPLOCK_LEVEL_NONE:
+ op->op_oplock_level = SMB_OPLOCK_NONE;
+ break;
+ case SMB2_OPLOCK_LEVEL_II:
+ op->op_oplock_level = SMB_OPLOCK_LEVEL_II;
+ break;
+ case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
+ op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
+ break;
+ case SMB2_OPLOCK_LEVEL_BATCH:
+ op->op_oplock_level = SMB_OPLOCK_BATCH;
+ break;
+ case SMB2_OPLOCK_LEVEL_LEASE:
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+ op->op_oplock_levelII = B_TRUE;
+
+ /*
+ * ImpersonationLevel (spec. says ignore)
+ * SmbCreateFlags (spec. says ignore)
+ */
+
+ if ((op->create_options & FILE_DELETE_ON_CLOSE) &&
+ !(op->desired_access & DELETE)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+ if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+
+ if (op->dattr & FILE_FLAG_WRITE_THROUGH)
+ op->create_options |= FILE_WRITE_THROUGH;
+ if (op->dattr & FILE_FLAG_DELETE_ON_CLOSE)
+ op->create_options |= FILE_DELETE_ON_CLOSE;
+ if (op->dattr & FILE_FLAG_BACKUP_SEMANTICS)
+ op->create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
+ if (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT)
+ sr->user_cr = smb_user_getprivcred(sr->uid_user);
+
+ /*
+ * If there is a "Create Context" payload, decode it.
+ * This may carry things like a security descriptor,
+ * extended attributes, etc. to be used in create.
+ *
+ * The create ctx buffer must start after the headers
+ * and file name, and must be 8-byte aligned.
+ */
+ if (CreateCtxLength != 0) {
+ if ((CreateCtxOffset & 7) != 0 ||
+ (CreateCtxOffset + sr->smb2_cmd_hdr) <
+ sr->smb_data.chain_offset) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+
+ rc = MBC_SHADOW_CHAIN(&cc_mbc, &sr->smb_data,
+ sr->smb2_cmd_hdr + CreateCtxOffset, CreateCtxLength);
+ if (rc) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+ status = smb2_decode_create_ctx(&cc_mbc, &cctx);
+ if (status)
+ goto errout;
+
+ if (cctx.cc_in_flags & CCTX_EA_BUFFER) {
+ status = NT_STATUS_EAS_NOT_SUPPORTED;
+ goto errout;
+ }
+
+ if (cctx.cc_in_flags & CCTX_SD_BUFFER) {
+ smb_sd_t sd;
+ cce = &cctx.cc_in_sec_desc;
+ status = smb_decode_sd(
+ &cce->cce_mbc, &sd);
+ if (status)
+ goto errout;
+ op->sd = kmem_alloc(sizeof (sd), KM_SLEEP);
+ *op->sd = sd;
+ }
+
+ if (cctx.cc_in_flags & CCTX_ALLOCATION_SIZE) {
+ cce = &cctx.cc_in_alloc_size;
+ rc = smb_mbc_decodef(&cce->cce_mbc, "q", &op->dsize);
+ if (rc) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+ }
+
+ /*
+ * Support for opening "Previous Versions".
+ * [MS-SMB2] 2.2.13.2.7 Data is an NT time.
+ */
+ if (cctx.cc_in_flags & CCTX_TIMEWARP_TOKEN) {
+ uint64_t timewarp;
+ cce = &cctx.cc_in_time_warp;
+ status = smb_mbc_decodef(&cce->cce_mbc,
+ "q", &timewarp);
+ if (status)
+ goto errout;
+ smb_time_nt_to_unix(timewarp, &op->timewarp);
+ op->create_timewarp = B_TRUE;
+ }
+ }
+
+ /*
+ * The real open call. Note: this gets attributes into
+ * op->fqi.fq_fattr (SMB_AT_ALL). We need those below.
+ */
+ status = smb_common_open(sr);
+ if (status != NT_STATUS_SUCCESS)
+ goto errout;
+ attr = &op->fqi.fq_fattr;
+
+ /*
+ * Convert the negotiate Oplock level back into
+ * SMB2 encoding form.
+ */
+ switch (op->op_oplock_level) {
+ default:
+ case SMB_OPLOCK_NONE:
+ OplockLevel = SMB2_OPLOCK_LEVEL_NONE;
+ break;
+ case SMB_OPLOCK_LEVEL_II:
+ OplockLevel = SMB2_OPLOCK_LEVEL_II;
+ break;
+ case SMB_OPLOCK_EXCLUSIVE:
+ OplockLevel = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+ break;
+ case SMB_OPLOCK_BATCH:
+ OplockLevel = SMB2_OPLOCK_LEVEL_BATCH;
+ break;
+ }
+
+ /*
+ * 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.
+ *
+ * Using sr->smb_fid as the file handle for now,
+ * though it could later be something larger,
+ * (16 bytes) similar to an NFSv4 open handle.
+ */
+ of = sr->fid_ofile;
+ smb2fid.persistent = 0;
+ smb2fid.temporal = sr->smb_fid;
+
+ switch (sr->tid_tree->t_res_type & STYPE_MASK) {
+ case STYPE_DISKTREE:
+ case STYPE_PRINTQ:
+ if (op->create_options & FILE_DELETE_ON_CLOSE)
+ smb_ofile_set_delete_on_close(of);
+ break;
+ }
+
+ /*
+ * Build the Create Context to return; first the
+ * per-element parts, then the aggregated buffer.
+ *
+ * No response for these:
+ * CCTX_EA_BUFFER
+ * CCTX_SD_BUFFER
+ * CCTX_ALLOCATION_SIZE
+ * CCTX_TIMEWARP_TOKEN
+ *
+ * We don't handle these yet.
+ * CCTX_DH_REQUEST
+ * CCTX_DH_RECONNECT
+ * CCTX_REQUEST_LEASE
+ */
+ if (cctx.cc_in_flags & CCTX_QUERY_MAX_ACCESS) {
+ cce = &cctx.cc_out_max_access;
+ uint32_t MaxAccess = 0;
+ if (of->f_node != NULL) {
+ smb_fsop_eaccess(sr, of->f_cr, of->f_node, &MaxAccess);
+ }
+ MaxAccess |= of->f_granted_access;
+ cce->cce_len = 8;
+ cce->cce_mbc.max_bytes = 8;
+ (void) smb_mbc_encodef(&cce->cce_mbc,
+ "ll", 0, MaxAccess);
+ cctx.cc_out_flags |= CCTX_QUERY_MAX_ACCESS;
+ }
+ if ((cctx.cc_in_flags & CCTX_QUERY_ON_DISK_ID) != 0 &&
+ of->f_node != NULL) {
+ cce = &cctx.cc_out_file_id;
+ fsid_t fsid;
+
+ fsid = SMB_NODE_FSID(of->f_node);
+
+ cce->cce_len = 32;
+ cce->cce_mbc.max_bytes = 32;
+ (void) smb_mbc_encodef(
+ &cce->cce_mbc, "qll.15.",
+ op->fileid, /* q */
+ fsid.val[0], /* l */
+ fsid.val[1]); /* l */
+ /* reserved (16 bytes) .15. */
+ cctx.cc_out_flags |= CCTX_QUERY_ON_DISK_ID;
+ }
+ if (cctx.cc_out_flags) {
+ sr->raw_data.max_bytes = smb2_max_trans;
+ status = smb2_encode_create_ctx(&sr->raw_data, &cctx);
+ if (status)
+ goto errout;
+ }
+
+ /*
+ * SMB2 Create reply
+ */
+ rc = smb_mbc_encodef(
+ &sr->reply,
+ "wb.lTTTTqqllqqll",
+ 89, /* StructSize */ /* w */
+ OplockLevel, /* b */
+ op->action_taken, /* l */
+ &attr->sa_crtime, /* T */
+ &attr->sa_vattr.va_atime, /* T */
+ &attr->sa_vattr.va_mtime, /* T */
+ &attr->sa_vattr.va_ctime, /* T */
+ attr->sa_allocsz, /* q */
+ attr->sa_vattr.va_size, /* q */
+ attr->sa_dosattr, /* l */
+ 0, /* reserved2 */ /* l */
+ smb2fid.persistent, /* q */
+ smb2fid.temporal, /* q */
+ 0, /* CreateCtxOffset l */
+ 0); /* CreateCtxLength l */
+ if (rc != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto errout;
+ }
+
+ CreateCtxOffset = sr->reply.chain_offset - sr->smb2_reply_hdr;
+ CreateCtxLength = MBC_LENGTH(&sr->raw_data);
+ if (CreateCtxLength != 0) {
+ /*
+ * Overwrite CreateCtxOffset, CreateCtxLength, pad
+ */
+ sr->reply.chain_offset -= 8;
+ rc = smb_mbc_encodef(
+ &sr->reply,
+ "ll#C",
+ CreateCtxOffset, /* l */
+ CreateCtxLength, /* l */
+ CreateCtxLength, /* # */
+ &sr->raw_data); /* C */
+ if (rc != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto errout;
+ }
+ } else {
+ (void) smb_mbc_encodef(&sr->reply, ".");
+ }
+ return (SDRC_SUCCESS);
+
+errout:
+ if (of != NULL)
+ smb_ofile_close(of, 0);
+ if (cctx.cc_out_flags)
+ smb2_free_create_ctx(&cctx);
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+}
+
+/*
+ * Decode an SMB2 Create Context buffer into our internal form.
+ * No policy decisions about what's supported here, just decode.
+ */
+static uint32_t
+smb2_decode_create_ctx(mbuf_chain_t *in_mbc, smb2_create_ctx_t *cc)
+{
+ smb2_create_ctx_elem_t *cce;
+ mbuf_chain_t name_mbc;
+ union {
+ uint32_t i;
+ char ch[4];
+ } cc_name;
+ uint32_t status;
+ int32_t next_off;
+ uint32_t data_len;
+ uint16_t data_off;
+ uint16_t name_off;
+ uint16_t name_len;
+ int top_offset;
+ int rc;
+
+ status = NT_STATUS_INVALID_PARAMETER;
+ for (;;) {
+ cce = NULL;
+ top_offset = in_mbc->chain_offset;
+ rc = smb_mbc_decodef(
+ in_mbc,
+ "lww..wl",
+ &next_off, /* l */
+ &name_off, /* w */
+ &name_len, /* w */
+ /* reserved .. */
+ &data_off, /* w */
+ &data_len); /* l */
+ if (rc)
+ break;
+
+ /*
+ * The Create Context "name", per [MS-SMB] 2.2.13.2
+ * They're defined as network-order integers for our
+ * switch below. We don't have routines to decode
+ * native order, so read as char[4] then ntohl.
+ * NB: in SMB3, some of these are 8 bytes.
+ */
+ if ((top_offset + name_off) < in_mbc->chain_offset)
+ break;
+ rc = MBC_SHADOW_CHAIN(&name_mbc, in_mbc,
+ top_offset + name_off, name_len);
+ if (rc)
+ break;
+ rc = smb_mbc_decodef(&name_mbc, "4c", &cc_name);
+ if (rc)
+ break;
+ cc_name.i = ntohl(cc_name.i);
+
+ switch (cc_name.i) {
+ case SMB2_CREATE_EA_BUFFER: /* ("ExtA") */
+ cc->cc_in_flags |= CCTX_EA_BUFFER;
+ cce = &cc->cc_in_ext_attr;
+ break;
+ case SMB2_CREATE_SD_BUFFER: /* ("SecD") */
+ cc->cc_in_flags |= CCTX_SD_BUFFER;
+ cce = &cc->cc_in_sec_desc;
+ break;
+ case SMB2_CREATE_DURABLE_HANDLE_REQUEST: /* ("DHnQ") */
+ cc->cc_in_flags |= CCTX_DH_REQUEST;
+ cce = &cc->cc_in_dh_request;
+ break;
+ case SMB2_CREATE_DURABLE_HANDLE_RECONNECT: /* ("DHnC") */
+ cc->cc_in_flags |= CCTX_DH_RECONNECT;
+ cce = &cc->cc_in_dh_reconnect;
+ break;
+ case SMB2_CREATE_ALLOCATION_SIZE: /* ("AISi") */
+ cc->cc_in_flags |= CCTX_ALLOCATION_SIZE;
+ cce = &cc->cc_in_alloc_size;
+ break;
+ case SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQ: /* ("MxAc") */
+ cc->cc_in_flags |= CCTX_QUERY_MAX_ACCESS;
+ /* no input data for this */
+ break;
+ case SMB2_CREATE_TIMEWARP_TOKEN: /* ("TWrp") */
+ cc->cc_in_flags |= CCTX_TIMEWARP_TOKEN;
+ cce = &cc->cc_in_time_warp;
+ break;
+ case SMB2_CREATE_QUERY_ON_DISK_ID: /* ("QFid") */
+ cc->cc_in_flags |= CCTX_QUERY_ON_DISK_ID;
+ /* no input data for this */
+ break;
+ case SMB2_CREATE_REQUEST_LEASE: /* ("RqLs") */
+ cc->cc_in_flags |= CCTX_REQUEST_LEASE;
+ cce = &cc->cc_in_req_lease;
+ break;
+ default:
+ /*
+ * Unknown create context values are normal, and
+ * should be ignored. However, in debug mode,
+ * let's log them so we know which ones we're
+ * not handling (and may want to add).
+ */
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "unknown create context ID 0x%x",
+ cc_name.i);
+#endif
+ cce = NULL;
+ break;
+ }
+
+ if (cce != NULL && data_len != 0) {
+ if ((data_off & 7) != 0)
+ break;
+ if ((top_offset + data_off) < in_mbc->chain_offset)
+ break;
+ rc = MBC_SHADOW_CHAIN(&cce->cce_mbc, in_mbc,
+ top_offset + data_off, data_len);
+ if (rc)
+ break;
+ cce->cce_len = data_len;
+ }
+
+ if (next_off == 0) {
+ /* Normal loop termination */
+ status = 0;
+ break;
+ }
+
+ if ((next_off & 7) != 0)
+ break;
+ if ((top_offset + next_off) < in_mbc->chain_offset)
+ break;
+ if ((top_offset + next_off) > in_mbc->max_bytes)
+ break;
+ in_mbc->chain_offset = top_offset + next_off;
+ }
+
+ return (status);
+}
+
+/*
+ * Encode an SMB2 Create Context buffer from our internal form.
+ */
+/* ARGSUSED */
+static uint32_t
+smb2_encode_create_ctx(mbuf_chain_t *mbc, smb2_create_ctx_t *cc)
+{
+ smb2_create_ctx_elem_t *cce;
+ int last_top = -1;
+ int rc;
+
+ if (cc->cc_out_flags & CCTX_QUERY_MAX_ACCESS) {
+ cce = &cc->cc_out_max_access;
+ last_top = mbc->chain_offset;
+ rc = smb2_encode_create_ctx_elem(mbc, cce,
+ SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQ);
+ 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_QUERY_ON_DISK_ID) {
+ cce = &cc->cc_out_file_id;
+ last_top = mbc->chain_offset;
+ rc = smb2_encode_create_ctx_elem(mbc, cce,
+ SMB2_CREATE_QUERY_ON_DISK_ID);
+ 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);
+
+ return (0);
+}
+
+static int
+smb2_encode_create_ctx_elem(mbuf_chain_t *out_mbc,
+ smb2_create_ctx_elem_t *cce, uint32_t id)
+{
+ union {
+ uint32_t i;
+ char ch[4];
+ } cc_name;
+ int rc;
+
+ /* as above */
+ cc_name.i = htonl(id);
+
+ /*
+ * This is the header, per [MS-SMB2] 2.2.13.2
+ * Sorry about the fixed offsets. We know we'll
+ * layout the data part as [name, payload] and
+ * name is a fixed length, so this easy.
+ * The final layout looks like this:
+ * a: this header (16 bytes)
+ * b: the name (4 bytes, 4 pad)
+ * c: the payload (variable)
+ *
+ * Note that "Next elem." is filled in later.
+ */
+ rc = smb_mbc_encodef(
+ out_mbc, "lwwwwl",
+ 0, /* Next offset l */
+ 16, /* NameOffset w */
+ 4, /* NameLength w */
+ 0, /* Reserved w */
+ 24, /* DataOffset w */
+ cce->cce_len); /* l */
+ if (rc)
+ return (rc);
+
+ /*
+ * Now the "name" and payload.
+ */
+ rc = smb_mbc_encodef(
+ out_mbc, "4c4.#C",
+ cc_name.ch, /* 4c4. */
+ cce->cce_len, /* # */
+ &cce->cce_mbc); /* C */
+
+ return (rc);
+}
+
+static void
+smb2_free_create_ctx(smb2_create_ctx_t *cc)
+{
+ smb2_create_ctx_elem_t *cce;
+
+ if (cc->cc_out_flags & CCTX_QUERY_MAX_ACCESS) {
+ cce = &cc->cc_out_max_access;
+ MBC_FLUSH(&cce->cce_mbc);
+ }
+ if (cc->cc_out_flags & CCTX_QUERY_ON_DISK_ID) {
+ cce = &cc->cc_out_file_id;
+ 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
new file mode 100644
index 0000000000..773a8f5f03
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c
@@ -0,0 +1,1269 @@
+/*
+ * 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 2015 Nexenta Systems, Inc. All rights reserved.
+ */
+
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_kstat.h>
+#include <smbsrv/smb2.h>
+
+/*
+ * Saved state for a command that "goes async". When a compound request
+ * contains a command that may block indefinitely, the compound reply is
+ * composed with an "interim response" for that command, and information
+ * needed to actually dispatch that command is saved on a list of "async"
+ * commands for this compound request. After the compound reply is sent,
+ * the list of async commands is processed, and those may block as long
+ * as they need to without affecting the initial compound request.
+ *
+ * Now interestingly, this "async" mechanism is not used with the full
+ * range of asynchrony that one might imagine. The design of async
+ * request processing can be drastically simplified if we can assume
+ * that there's no need to run more than one async command at a time.
+ * With that simplifying assumption, we can continue using the current
+ * "one worker thread per request message" model, which has very simple
+ * locking rules etc. The same worker thread that handles the initial
+ * compound request can handle the list of async requests.
+ *
+ * As it turns out, SMB2 clients do not try to use more than one "async"
+ * command in a compound. If they were to do so, the [MS-SMB2] spec.
+ * allows us to decline additional async requests with an error.
+ *
+ * smb_async_req_t is the struct used to save an "async" request on
+ * the list of requests that had an interim reply in the initial
+ * compound reply. This includes everything needed to restart
+ * processing at the async command.
+ */
+
+typedef struct smb2_async_req {
+
+ smb_sdrc_t (*ar_func)(smb_request_t *);
+
+ int ar_cmd_hdr; /* smb2_cmd_hdr offset */
+ int ar_cmd_len; /* length from hdr */
+
+ /*
+ * SMB2 header fields.
+ */
+ 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 *);
+smb_sdrc_t smb2_invalid_cmd(smb_request_t *);
+static void smb2_tq_work(void *);
+
+static const smb_disp_entry_t const
+smb2_disp_table[SMB2__NCMDS] = {
+
+ /* text-name, pre, func, post, cmd-code, dialect, flags */
+
+ { "smb2_negotiate", NULL,
+ smb2_negotiate, NULL, 0, 0,
+ SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
+
+ { "smb2_session_setup", NULL,
+ smb2_session_setup, NULL, 0, 0,
+ SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
+
+ { "smb2_logoff", NULL,
+ smb2_logoff, NULL, 0, 0,
+ SDDF_SUPPRESS_TID },
+
+ { "smb2_tree_connect", NULL,
+ smb2_tree_connect, NULL, 0, 0,
+ SDDF_SUPPRESS_TID },
+
+ { "smb2_tree_disconn", NULL,
+ smb2_tree_disconn, NULL, 0, 0 },
+
+ { "smb2_create", NULL,
+ smb2_create, NULL, 0, 0 },
+
+ { "smb2_close", NULL,
+ smb2_close, NULL, 0, 0 },
+
+ { "smb2_flush", NULL,
+ smb2_flush, NULL, 0, 0 },
+
+ { "smb2_read", NULL,
+ smb2_read, NULL, 0, 0 },
+
+ { "smb2_write", NULL,
+ smb2_write, NULL, 0, 0 },
+
+ { "smb2_lock", NULL,
+ smb2_lock, NULL, 0, 0 },
+
+ { "smb2_ioctl", NULL,
+ smb2_ioctl, NULL, 0, 0 },
+
+ /*
+ * Note: Cancel gets the "invalid command" handler because
+ * that's always handled directly in the reader. We should
+ * never get to the function using this table, but note:
+ * We CAN get here if a nasty client adds cancel to some
+ * compound message, which is a protocol violation.
+ */
+ { "smb2_cancel", NULL,
+ smb2_invalid_cmd, NULL, 0, 0 },
+
+ { "smb2_echo", NULL,
+ smb2_echo, NULL, 0, 0,
+ SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
+
+ { "smb2_query_dir", NULL,
+ smb2_query_dir, NULL, 0, 0 },
+
+ { "smb2_change_notify", NULL,
+ smb2_change_notify, NULL, 0, 0 },
+
+ { "smb2_query_info", NULL,
+ smb2_query_info, NULL, 0, 0 },
+
+ { "smb2_set_info", NULL,
+ smb2_set_info, NULL, 0, 0 },
+
+ { "smb2_oplock_break_ack", NULL,
+ smb2_oplock_break_ack, NULL, 0, 0 },
+
+ { "smb2_invalid_cmd", NULL,
+ smb2_invalid_cmd, NULL, 0, 0,
+ SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
+};
+
+smb_sdrc_t
+smb2_invalid_cmd(smb_request_t *sr)
+{
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "clnt %s bad SMB2 cmd code",
+ sr->session->ip_addr_str);
+#endif
+ sr->smb2_status = NT_STATUS_INVALID_PARAMETER;
+ return (SDRC_DROP_VC);
+}
+
+/*
+ * This is the SMB2 handler for new smb requests, called from
+ * smb_session_reader after SMB negotiate is done. For most SMB2
+ * requests, we just enqueue them for the smb_session_worker to
+ * execute via the task queue, so they can block for resources
+ * without stopping the reader thread. A few protocol messages
+ * are special cases and are handled directly here in the reader
+ * thread so they don't wait for taskq scheduling.
+ *
+ * This function must either enqueue the new request for
+ * execution via the task queue, or execute it directly
+ * and then free it. If this returns non-zero, the caller
+ * will drop the session.
+ */
+int
+smb2sr_newrq(smb_request_t *sr)
+{
+ uint32_t magic;
+ uint16_t command;
+ int rc;
+
+ magic = LE_IN32(sr->sr_request_buf);
+ if (magic != SMB2_PROTOCOL_MAGIC) {
+ smb_request_free(sr);
+ /* will drop the connection */
+ return (EPROTO);
+ }
+
+ /*
+ * Execute Cancel requests immediately, (here in the
+ * reader thread) so they won't wait for any other
+ * commands we might already have in the task queue.
+ * Cancel also skips signature verification and
+ * does not consume a sequence number.
+ * [MS-SMB2] 3.2.4.24 Cancellation...
+ */
+ command = LE_IN16((uint8_t *)sr->sr_request_buf + 12);
+ if (command == SMB2_CANCEL) {
+ rc = smb2sr_newrq_cancel(sr);
+ smb_request_free(sr);
+ return (rc);
+ }
+
+ /*
+ * Submit the request to the task queue, which calls
+ * smb2_tq_work when the workload permits.
+ */
+ sr->sr_time_submitted = gethrtime();
+ sr->sr_state = SMB_REQ_STATE_SUBMITTED;
+ smb_srqueue_waitq_enter(sr->session->s_srqueue);
+ (void) taskq_dispatch(sr->sr_server->sv_worker_pool,
+ smb2_tq_work, sr, TQ_SLEEP);
+
+ return (0);
+}
+
+static void
+smb2_tq_work(void *arg)
+{
+ smb_request_t *sr;
+ smb_srqueue_t *srq;
+
+ sr = (smb_request_t *)arg;
+ SMB_REQ_VALID(sr);
+
+ srq = sr->session->s_srqueue;
+ smb_srqueue_waitq_to_runq(srq);
+ sr->sr_worker = curthread;
+ sr->sr_time_active = gethrtime();
+
+ /*
+ * In contrast with SMB1, SMB2 must _always_ dispatch to
+ * the handler function, because cancelled requests need
+ * an error reply (NT_STATUS_CANCELLED).
+ */
+ smb2sr_work(sr);
+
+ smb_srqueue_runq_exit(srq);
+}
+
+/*
+ * smb2sr_work
+ *
+ * This function processes each SMB command in the current request
+ * (which may be a compound request) building a reply containing
+ * SMB reply messages, one-to-one with the SMB commands. Some SMB
+ * commands (change notify, blocking locks) may require both an
+ * "interim response" and a later "async response" at completion.
+ * In such cases, we'll encode the interim response in the reply
+ * compound we're building, and put the (now async) command on a
+ * list of commands that need further processing. After we've
+ * finished processing the commands in this compound and building
+ * the compound reply, we'll send the compound reply, and finally
+ * process the list of async commands.
+ *
+ * As we work our way through the compound request and reply,
+ * we need to keep track of the bounds of the current request
+ * and reply. For the request, this uses an MBC_SHADOW_CHAIN
+ * that begins at smb2_cmd_hdr. The reply is appended to the
+ * sr->reply chain starting at smb2_reply_hdr.
+ *
+ * This function must always free the smb request.
+ */
+void
+smb2sr_work(struct smb_request *sr)
+{
+ const smb_disp_entry_t *sdd;
+ smb_disp_stats_t *sds;
+ smb_session_t *session;
+ uint32_t msg_len;
+ uint16_t cmd_idx;
+ int rc = 0;
+ boolean_t disconnect = B_FALSE;
+ boolean_t related;
+
+ session = sr->session;
+
+ ASSERT(sr->tid_tree == 0);
+ ASSERT(sr->uid_user == 0);
+ ASSERT(sr->fid_ofile == 0);
+ sr->smb_fid = (uint16_t)-1;
+ sr->smb2_status = 0;
+
+ /* temporary until we identify a user */
+ sr->user_cr = zone_kcred();
+
+ mutex_enter(&sr->sr_mutex);
+ switch (sr->sr_state) {
+ case SMB_REQ_STATE_SUBMITTED:
+ case SMB_REQ_STATE_CLEANED_UP:
+ sr->sr_state = SMB_REQ_STATE_ACTIVE;
+ break;
+ default:
+ ASSERT(0);
+ /* FALLTHROUGH */
+ case SMB_REQ_STATE_CANCELED:
+ sr->smb2_status = NT_STATUS_CANCELLED;
+ break;
+ }
+ mutex_exit(&sr->sr_mutex);
+
+cmd_start:
+ /*
+ * Decode the request header
+ *
+ * Most problems with decoding will result in the error
+ * STATUS_INVALID_PARAMETER. If the decoding problem
+ * prevents continuing, we'll close the connection.
+ * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
+ *
+ * We treat some status codes as if "sticky", meaning
+ * once they're set after some command handler returns,
+ * all remaining commands get this status without even
+ * calling the command-specific handler. The cancelled
+ * status is used above, and insufficient_resources is
+ * used when smb2sr_go_async declines to "go async".
+ * Otherwise initialize to zero (success).
+ */
+ if (sr->smb2_status != NT_STATUS_CANCELLED &&
+ sr->smb2_status != NT_STATUS_INSUFFICIENT_RESOURCES)
+ sr->smb2_status = 0;
+
+ sr->smb2_cmd_hdr = sr->command.chain_offset;
+ if ((rc = smb2_decode_header(sr)) != 0) {
+ cmn_err(CE_WARN, "clnt %s bad SMB2 header",
+ session->ip_addr_str);
+ disconnect = B_TRUE;
+ goto cleanup;
+ }
+
+ /*
+ * The SMB2_FLAGS_SERVER_TO_REDIR should only appear
+ * in messages from the server back to the client.
+ */
+ if ((sr->smb2_hdr_flags & SMB2_FLAGS_SERVER_TO_REDIR) != 0) {
+ cmn_err(CE_WARN, "clnt %s bad SMB2 flags",
+ session->ip_addr_str);
+ disconnect = B_TRUE;
+ goto cleanup;
+ }
+ related = (sr->smb2_hdr_flags & SMB2_FLAGS_RELATED_OPERATIONS);
+
+ /*
+ * In case we bail out with an error before we get to the
+ * section that computes the credit grant, initialize the
+ * response header fields so that credits won't change.
+ * Note: SMB 2.02 clients may send credit charge zero.
+ */
+ if (sr->smb2_credit_charge == 0)
+ sr->smb2_credit_charge = 1;
+ sr->smb2_credit_response = sr->smb2_credit_charge;
+
+ /*
+ * Reserve space for the reply header, and save the offset.
+ * The reply header will be overwritten later. If we have
+ * already exhausted the output space, then this client is
+ * trying something funny. Log it and kill 'em.
+ */
+ sr->smb2_reply_hdr = sr->reply.chain_offset;
+ if ((rc = smb2_encode_header(sr, B_FALSE)) != 0) {
+ cmn_err(CE_WARN, "clnt %s excessive reply",
+ session->ip_addr_str);
+ disconnect = B_TRUE;
+ goto cleanup;
+ }
+
+ /*
+ * Figure out the length of data following the SMB2 header.
+ * It ends at either the next SMB2 header if there is one
+ * (smb2_next_command != 0) or at the end of the message.
+ */
+ if (sr->smb2_next_command != 0) {
+ /* [MS-SMB2] says this is 8-byte aligned */
+ msg_len = sr->smb2_next_command;
+ if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) ||
+ ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) {
+ cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd",
+ session->ip_addr_str);
+ disconnect = B_TRUE;
+ goto cleanup;
+ }
+ } else {
+ msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr;
+ }
+
+ /*
+ * Setup a shadow chain for this SMB2 command, starting
+ * with the header and ending at either the next command
+ * or the end of the message. The signing check below
+ * needs the entire SMB2 command. After that's done, we
+ * advance chain_offset to the end of the header where
+ * the command specific handlers continue decoding.
+ */
+ (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
+ sr->smb2_cmd_hdr, msg_len);
+
+ /*
+ * Validate the commmand code, get dispatch table entries.
+ * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
+ *
+ * The last slot in the dispatch table is used to handle
+ * invalid commands. Same for statistics.
+ */
+ if (sr->smb2_cmd_code < SMB2_INVALID_CMD)
+ cmd_idx = sr->smb2_cmd_code;
+ else
+ cmd_idx = SMB2_INVALID_CMD;
+ sdd = &smb2_disp_table[cmd_idx];
+ sds = &session->s_server->sv_disp_stats2[cmd_idx];
+
+ /*
+ * If this command is NOT "related" to the previous,
+ * clear out the UID, TID, FID state that might be
+ * left over from the previous command.
+ *
+ * If the command IS related, any new IDs are ignored,
+ * and we simply continue with the previous user, tree,
+ * and open file.
+ */
+ if (!related) {
+ /*
+ * Drop user, tree, file; carefully ordered to
+ * avoid dangling references: file, tree, user
+ */
+ if (sr->fid_ofile != NULL) {
+ smb_ofile_request_complete(sr->fid_ofile);
+ smb_ofile_release(sr->fid_ofile);
+ sr->fid_ofile = NULL;
+ }
+ if (sr->tid_tree != NULL) {
+ smb_tree_release(sr->tid_tree);
+ sr->tid_tree = NULL;
+ }
+ if (sr->uid_user != NULL) {
+ smb_user_release(sr->uid_user);
+ sr->uid_user = NULL;
+ sr->user_cr = zone_kcred();
+ }
+ }
+
+ /*
+ * Make sure we have a user and tree as needed
+ * according to the flags for the this command.
+ * Note that we may have inherited these.
+ */
+ if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0) {
+ /*
+ * This command requires a user session.
+ */
+ if (related) {
+ /*
+ * Previous command should have given us a user.
+ * [MS-SMB2] 3.3.5.2 Handling Related Requests
+ */
+ if (sr->uid_user == NULL) {
+ smb2sr_put_error(sr,
+ NT_STATUS_INVALID_PARAMETER);
+ goto cmd_done;
+ }
+ sr->smb_uid = sr->uid_user->u_uid;
+ } 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);
+ if (sr->uid_user == NULL) {
+ smb2sr_put_error(sr,
+ NT_STATUS_USER_SESSION_DELETED);
+ goto cmd_done;
+ }
+ sr->user_cr = smb_user_getcred(sr->uid_user);
+ }
+ ASSERT(sr->uid_user != NULL);
+ }
+
+ if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0) {
+ /*
+ * This command requires a tree connection.
+ */
+ if (related) {
+ /*
+ * Previous command should have given us a tree.
+ * [MS-SMB2] 3.3.5.2 Handling Related Requests
+ */
+ if (sr->tid_tree == NULL) {
+ smb2sr_put_error(sr,
+ NT_STATUS_INVALID_PARAMETER);
+ goto cmd_done;
+ }
+ sr->smb_tid = sr->tid_tree->t_tid;
+ } else {
+ /*
+ * Lookup the TID
+ * [MS-SMB2] 3.3.5.2 Verifying the Tree Connect
+ */
+ ASSERT(sr->tid_tree == NULL);
+ sr->tid_tree = smb_session_lookup_tree(session,
+ sr->smb_tid);
+ if (sr->tid_tree == NULL) {
+ smb2sr_put_error(sr,
+ NT_STATUS_NETWORK_NAME_DELETED);
+ goto cmd_done;
+ }
+ }
+ ASSERT(sr->tid_tree != NULL);
+ }
+
+ /*
+ * SMB2 signature verification, two parts:
+ * (a) Require SMB2_FLAGS_SIGNED (for most request types)
+ * (b) If SMB2_FLAGS_SIGNED is set, check the signature.
+ * [MS-SMB2] 3.3.5.2.4 Verifying the Signature
+ */
+
+ /*
+ * No user session means no signature check. That's OK,
+ * i.e. for commands marked SDDF_SUPPRESS_UID above.
+ * Note, this also means we won't sign the reply.
+ */
+ if (sr->uid_user == NULL)
+ sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED;
+
+ /*
+ * The SDDF_SUPPRESS_UID dispatch is set for requests that
+ * don't need a UID (user). These also don't require a
+ * signature check here.
+ */
+ if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 &&
+ sr->uid_user != NULL &&
+ (sr->uid_user->u_sign_flags & SMB_SIGNING_CHECK) != 0) {
+ /*
+ * This request type should be signed, and
+ * we're configured to require signatures.
+ */
+ if ((sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) == 0) {
+ smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED);
+ goto cmd_done;
+ }
+ rc = smb2_sign_check_request(sr);
+ if (rc != 0) {
+ DTRACE_PROBE1(smb2__sign__check, smb_request_t, sr);
+ smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED);
+ goto cmd_done;
+ }
+ }
+
+ /*
+ * Now that the signing check is done with smb_data,
+ * advance past the SMB2 header we decoded earlier.
+ * This leaves sr->smb_data correctly positioned
+ * for command-specific decoding in the dispatch
+ * function called next.
+ */
+ sr->smb_data.chain_offset = sr->smb2_cmd_hdr + SMB2_HDR_SIZE;
+
+ /*
+ * SMB2 credits determine how many simultaneous commands the
+ * client may issue, and bounds the range of message IDs those
+ * commands may use. With multi-credit support, commands may
+ * use ranges of message IDs, where the credits used by each
+ * command are proportional to their data transfer size.
+ *
+ * Every command may request an increase or decrease of
+ * the currently granted credits, based on the difference
+ * between the credit request and the credit charge.
+ * [MS-SMB2] 3.3.1.2 Algorithm for the Granting of Credits
+ *
+ * Most commands have credit_request=1, credit_charge=1,
+ * which keeps the credit grant unchanged.
+ *
+ * All we're really doing here (for now) is reducing the
+ * credit_response if the client requests a credit increase
+ * that would take their credit over the maximum, and
+ * limiting the decrease so they don't run out of credits.
+ *
+ * Later, this could do something dynamic based on load.
+ *
+ * One other non-obvious bit about credits: We keep the
+ * session s_max_credits low until the 1st authentication,
+ * at which point we'll set the normal maximum_credits.
+ * Some clients ask for more credits with session setup,
+ * and we need to handle that requested increase _after_
+ * the command-specific handler returns so it won't be
+ * restricted to the lower (pre-auth) limit.
+ */
+ sr->smb2_credit_response = sr->smb2_credit_request;
+ if (sr->smb2_credit_request < sr->smb2_credit_charge) {
+ uint16_t cur, d;
+
+ mutex_enter(&session->s_credits_mutex);
+ cur = session->s_cur_credits;
+
+ /* Handle credit decrease. */
+ d = sr->smb2_credit_charge - sr->smb2_credit_request;
+ cur -= d;
+ if (cur & 0x8000) {
+ /*
+ * underflow (bad credit charge or request)
+ * leave credits unchanged (response=charge)
+ */
+ cur = session->s_cur_credits;
+ sr->smb2_credit_response = sr->smb2_credit_charge;
+ DTRACE_PROBE1(smb2__credit__neg, smb_request_t, sr);
+ }
+
+ /*
+ * The server MUST ensure that the number of credits
+ * held by the client is never reduced to zero.
+ * [MS-SMB2] 3.3.1.2
+ */
+ if (cur == 0) {
+ cur = 1;
+ sr->smb2_credit_response += 1;
+ DTRACE_PROBE1(smb2__credit__min, smb_request_t, sr);
+ }
+
+ DTRACE_PROBE3(smb2__credit__decrease,
+ smb_request_t, sr, int, (int)cur,
+ int, (int)session->s_cur_credits);
+
+ session->s_cur_credits = cur;
+ mutex_exit(&session->s_credits_mutex);
+ }
+
+ /*
+ * The real work: call the SMB2 command handler
+ * (except for "sticky" smb2_status - see above)
+ */
+ sr->sr_time_start = gethrtime();
+ rc = SDRC_SUCCESS;
+ if (sr->smb2_status == 0) {
+ /* NB: not using pre_op */
+ rc = (*sdd->sdt_function)(sr);
+ /* NB: not using post_op */
+ }
+
+ MBC_FLUSH(&sr->raw_data);
+
+ /*
+ * Second half of SMB2 credit handling (increases)
+ */
+ if (sr->smb2_credit_request > sr->smb2_credit_charge) {
+ uint16_t cur, d;
+
+ mutex_enter(&session->s_credits_mutex);
+ cur = session->s_cur_credits;
+
+ /* Handle credit increase. */
+ d = sr->smb2_credit_request - sr->smb2_credit_charge;
+ cur += d;
+
+ /*
+ * If new credits would be above max,
+ * reduce the credit grant.
+ */
+ if (cur > session->s_max_credits) {
+ d = cur - session->s_max_credits;
+ cur = session->s_max_credits;
+ sr->smb2_credit_response -= d;
+ DTRACE_PROBE1(smb2__credit__max, smb_request_t, sr);
+ }
+
+ DTRACE_PROBE3(smb2__credit__increase,
+ smb_request_t, sr, int, (int)cur,
+ int, (int)session->s_cur_credits);
+
+ session->s_cur_credits = cur;
+ mutex_exit(&session->s_credits_mutex);
+ }
+
+cmd_done:
+ /*
+ * Pad the reply to align(8) if necessary.
+ */
+ if (sr->reply.chain_offset & 7) {
+ int padsz = 8 - (sr->reply.chain_offset & 7);
+ (void) smb_mbc_encodef(&sr->reply, "#.", padsz);
+ }
+ ASSERT((sr->reply.chain_offset & 7) == 0);
+
+ /*
+ * Record some statistics: latency, rx bytes, tx bytes.
+ */
+ smb_latency_add_sample(&sds->sdt_lat,
+ gethrtime() - sr->sr_time_start);
+ atomic_add_64(&sds->sdt_rxb,
+ (int64_t)(sr->command.chain_offset - sr->smb2_cmd_hdr));
+ atomic_add_64(&sds->sdt_txb,
+ (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr));
+
+ switch (rc) {
+ case SDRC_SUCCESS:
+ break;
+ default:
+ /*
+ * SMB2 does not use the other dispatch return codes.
+ * If we see something else, log an event so we'll
+ * know something is returning bogus status codes.
+ * If you see these in the log, use dtrace to find
+ * the code returning something else.
+ */
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "handler for %u returned 0x%x",
+ sr->smb2_cmd_code, rc);
+#endif
+ /* FALLTHROUGH */
+ case SDRC_ERROR:
+ if (sr->smb2_status == 0)
+ sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ case SDRC_DROP_VC:
+ disconnect = B_TRUE;
+ goto cleanup;
+ }
+
+ /*
+ * If there's a next command, figure out where it starts,
+ * and fill in the next command offset for the reply.
+ * Note: We sanity checked smb2_next_command above
+ * (the offset to the next command). Similarly set
+ * smb2_next_reply as the offset to the next reply.
+ */
+ if (sr->smb2_next_command != 0) {
+ sr->command.chain_offset =
+ sr->smb2_cmd_hdr + sr->smb2_next_command;
+ sr->smb2_next_reply =
+ sr->reply.chain_offset - sr->smb2_reply_hdr;
+ } else {
+ sr->smb2_next_reply = 0;
+ }
+
+ /*
+ * Overwrite the SMB2 header for the response of
+ * this command (possibly part of a compound).
+ * encode_header adds: SMB2_FLAGS_SERVER_TO_REDIR
+ */
+ (void) smb2_encode_header(sr, B_TRUE);
+
+ if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED)
+ smb2_sign_reply(sr);
+
+ if (sr->smb2_next_command != 0)
+ goto cmd_start;
+
+ /*
+ * We've done all the commands in this compound.
+ * Send it out.
+ */
+ smb2_send_reply(sr);
+
+ /*
+ * If any of the requests "went async", process those now.
+ * The async. function "keeps" this sr, changing its state
+ * to completed and calling smb_request_free().
+ */
+ if (sr->sr_async_req != NULL) {
+ smb2sr_do_async(sr);
+ return;
+ }
+
+cleanup:
+ if (disconnect) {
+ smb_rwx_rwenter(&session->s_lock, RW_WRITER);
+ switch (session->s_state) {
+ case SMB_SESSION_STATE_DISCONNECTED:
+ case SMB_SESSION_STATE_TERMINATED:
+ break;
+ default:
+ smb_soshutdown(session->sock);
+ session->s_state = SMB_SESSION_STATE_DISCONNECTED;
+ break;
+ }
+ smb_rwx_rwexit(&session->s_lock);
+ }
+
+ mutex_enter(&sr->sr_mutex);
+ sr->sr_state = SMB_REQ_STATE_COMPLETED;
+ mutex_exit(&sr->sr_mutex);
+
+ smb_request_free(sr);
+}
+
+/*
+ * Dispatch an async request using saved information.
+ * See smb2sr_save_async and [MS-SMB2] 3.3.4.2
+ *
+ * This is sort of a "lite" version of smb2sr_work. Initialize the
+ * command and reply areas as they were when the command-speicific
+ * handler started (in case it needs to decode anything again).
+ * Call the async function, which builds the command-specific part
+ * of the response. Finally, send the response and free the sr.
+ */
+void
+smb2sr_do_async(smb_request_t *sr)
+{
+ const smb_disp_entry_t *sdd;
+ smb_disp_stats_t *sds;
+ smb2_async_req_t *ar;
+ int rc = 0;
+
+ /*
+ * Restore what smb2_decode_header found.
+ * (In lieu of decoding it again.)
+ */
+ ar = sr->sr_async_req;
+ sr->smb2_cmd_hdr = ar->ar_cmd_hdr;
+ sr->smb2_cmd_code = ar->ar_cmd_code;
+ sr->smb2_hdr_flags = ar->ar_hdr_flags;
+ sr->smb2_async_id = (uintptr_t)ar;
+ 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_status = 0;
+
+ /*
+ * Async requests don't grant credits, because any credits
+ * should have gone out with the interim reply.
+ * An async reply goes alone (no next reply).
+ */
+ sr->smb2_credit_response = 0;
+ sr->smb2_next_reply = 0;
+
+ /*
+ * Setup input mbuf_chain
+ */
+ ASSERT(ar->ar_cmd_len >= SMB2_HDR_SIZE);
+ (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
+ sr->smb2_cmd_hdr + SMB2_HDR_SIZE,
+ ar->ar_cmd_len - SMB2_HDR_SIZE);
+
+ /*
+ * Setup output mbuf_chain
+ */
+ MBC_FLUSH(&sr->reply);
+ sr->smb2_reply_hdr = sr->reply.chain_offset;
+ (void) smb2_encode_header(sr, B_FALSE);
+
+ VERIFY3U(sr->smb2_cmd_code, <, SMB2_INVALID_CMD);
+ sdd = &smb2_disp_table[sr->smb2_cmd_code];
+ sds = sr->session->s_server->sv_disp_stats2;
+ sds = &sds[sr->smb2_cmd_code];
+
+ /*
+ * Keep the UID, TID, ofile we have.
+ */
+ if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 &&
+ sr->uid_user == NULL) {
+ smb2sr_put_error(sr, NT_STATUS_USER_SESSION_DELETED);
+ goto cmd_done;
+ }
+ if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0 &&
+ sr->tid_tree == NULL) {
+ smb2sr_put_error(sr, NT_STATUS_NETWORK_NAME_DELETED);
+ goto cmd_done;
+ }
+
+ /*
+ * Signature already verified
+ * Credits handled...
+ *
+ * Just call the async handler function.
+ */
+ rc = ar->ar_func(sr);
+ if (rc != 0 && sr->smb2_status == 0)
+ sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
+
+cmd_done:
+ /*
+ * Pad the reply to align(8) if necessary.
+ */
+ if (sr->reply.chain_offset & 7) {
+ int padsz = 8 - (sr->reply.chain_offset & 7);
+ (void) smb_mbc_encodef(&sr->reply, "#.", padsz);
+ }
+ ASSERT((sr->reply.chain_offset & 7) == 0);
+
+ /*
+ * Record some statistics: (just tx bytes here)
+ */
+ atomic_add_64(&sds->sdt_txb,
+ (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr));
+
+ /*
+ * Overwrite the SMB2 header for the response of
+ * this command (possibly part of a compound).
+ * The call adds: SMB2_FLAGS_SERVER_TO_REDIR
+ */
+ (void) smb2_encode_header(sr, B_TRUE);
+
+ if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED)
+ smb2_sign_reply(sr);
+
+ smb2_send_reply(sr);
+
+ /*
+ * Done. Unlink and free.
+ */
+ sr->sr_async_req = NULL;
+ kmem_free(ar, sizeof (*ar));
+
+ mutex_enter(&sr->sr_mutex);
+ sr->sr_state = SMB_REQ_STATE_COMPLETED;
+ mutex_exit(&sr->sr_mutex);
+
+ smb_request_free(sr);
+}
+
+/*
+ * In preparation for sending an "interim response", save
+ * all the state we'll need to run an async command later,
+ * and assign an "async id" for this (now async) command.
+ * See [MS-SMB2] 3.3.4.2
+ *
+ * If more than one request in a compound request tries to
+ * "go async", we can "say no". See [MS-SMB2] 3.3.4.2
+ * If an operation would require asynchronous processing
+ * but resources are constrained, the server MAY choose to
+ * fail that operation with STATUS_INSUFFICIENT_RESOURCES.
+ *
+ * For simplicity, we further restrict the cases where we're
+ * willing to "go async", and only allow the last command in a
+ * compound to "go async". It happens that this is the only
+ * case where we're actually asked to go async anyway. This
+ * simplification also means there can be at most one command
+ * in a compound that "goes async" (the last one).
+ *
+ * If we agree to "go async", this should return STATUS_PENDING.
+ * Otherwise return STATUS_INSUFFICIENT_RESOURCES for this and
+ * all requests following this request. (See the comments re.
+ * "sticky" smb2_status values in smb2sr_work).
+ *
+ * Note: the Async ID we assign here is arbitrary, and need only
+ * be unique among pending async responses on this connection, so
+ * this just uses an object address as the Async ID.
+ *
+ * Also, the assigned worker is the ONLY thread using this
+ * async request object (sr_async_req) so no locking.
+ */
+uint32_t
+smb2sr_go_async(smb_request_t *sr,
+ smb_sdrc_t (*async_func)(smb_request_t *))
+{
+ smb2_async_req_t *ar;
+
+ if (sr->smb2_next_command != 0)
+ return (NT_STATUS_INSUFFICIENT_RESOURCES);
+
+ ASSERT(sr->sr_async_req == NULL);
+ ar = kmem_zalloc(sizeof (*ar), KM_SLEEP);
+
+ /*
+ * Place an interim response in the compound reply.
+ *
+ * Turn on the "async" flag for both the (synchronous)
+ * interim response and the (later) async response,
+ * by storing that in flags before coping into ar.
+ *
+ * The "related" flag should always be off for the
+ * async part because we're no longer operating on a
+ * sequence of commands when we execute that.
+ */
+ sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND;
+ sr->smb2_async_id = (uintptr_t)ar;
+
+ ar->ar_func = async_func;
+ ar->ar_cmd_hdr = sr->smb2_cmd_hdr;
+ ar->ar_cmd_len = sr->smb_data.max_bytes - sr->smb2_cmd_hdr;
+
+ ar->ar_cmd_code = sr->smb2_cmd_code;
+ ar->ar_hdr_flags = sr->smb2_hdr_flags &
+ ~SMB2_FLAGS_RELATED_OPERATIONS;
+ ar->ar_messageid = sr->smb2_messageid;
+ ar->ar_pid = sr->smb_pid;
+ ar->ar_tid = sr->smb_tid;
+ ar->ar_uid = sr->smb_uid;
+
+ sr->sr_async_req = ar;
+
+ /* Interim responses are NOT signed. */
+ sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED;
+
+ return (NT_STATUS_PENDING);
+}
+
+int
+smb2_decode_header(smb_request_t *sr)
+{
+ uint64_t ssnid;
+ uint32_t pid, tid;
+ uint16_t hdr_len;
+ int rc;
+
+ rc = smb_mbc_decodef(
+ &sr->command, "Nwww..wwllqllq16c",
+ &hdr_len, /* w */
+ &sr->smb2_credit_charge, /* w */
+ &sr->smb2_chan_seq, /* w */
+ /* reserved .. */
+ &sr->smb2_cmd_code, /* w */
+ &sr->smb2_credit_request, /* w */
+ &sr->smb2_hdr_flags, /* l */
+ &sr->smb2_next_command, /* l */
+ &sr->smb2_messageid, /* q */
+ &pid, /* l */
+ &tid, /* l */
+ &ssnid, /* q */
+ sr->smb2_sig); /* 16c */
+ if (rc)
+ return (rc);
+
+ if (hdr_len != SMB2_HDR_SIZE)
+ return (-1);
+
+ sr->smb_uid = (uint16_t)ssnid; /* XXX wide UIDs */
+
+ if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
+ sr->smb2_async_id = pid |
+ ((uint64_t)tid) << 32;
+ } else {
+ sr->smb_pid = pid;
+ sr->smb_tid = (uint16_t)tid; /* XXX wide TIDs */
+ }
+
+ return (rc);
+}
+
+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;
+
+ if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
+ pid_tid_aid = sr->smb2_async_id;
+ } else {
+ pid_tid_aid = sr->smb_pid |
+ ((uint64_t)sr->smb_tid) << 32;
+ }
+ reply_hdr_flags = sr->smb2_hdr_flags | SMB2_FLAGS_SERVER_TO_REDIR;
+
+ if (overwrite) {
+ rc = smb_mbc_poke(&sr->reply,
+ sr->smb2_reply_hdr,
+ "Nwwlwwllqqq16c",
+ SMB2_HDR_SIZE, /* w */
+ sr->smb2_credit_charge, /* w */
+ sr->smb2_status, /* l */
+ sr->smb2_cmd_code, /* w */
+ sr->smb2_credit_response, /* w */
+ reply_hdr_flags, /* l */
+ sr->smb2_next_reply, /* l */
+ sr->smb2_messageid, /* q */
+ pid_tid_aid, /* q */
+ ssnid, /* q */
+ sr->smb2_sig); /* 16c */
+ } else {
+ rc = smb_mbc_encodef(&sr->reply,
+ "Nwwlwwllqqq16c",
+ SMB2_HDR_SIZE, /* w */
+ sr->smb2_credit_charge, /* w */
+ sr->smb2_status, /* l */
+ sr->smb2_cmd_code, /* w */
+ sr->smb2_credit_response, /* w */
+ reply_hdr_flags, /* l */
+ sr->smb2_next_reply, /* l */
+ sr->smb2_messageid, /* q */
+ pid_tid_aid, /* q */
+ ssnid, /* q */
+ sr->smb2_sig); /* 16c */
+ }
+
+ return (rc);
+}
+
+void
+smb2_send_reply(smb_request_t *sr)
+{
+
+ if (smb_session_send(sr->session, 0, &sr->reply) == 0)
+ sr->reply.chain = 0;
+}
+
+/*
+ * This wrapper function exists to help catch calls to smbsr_status()
+ * (which is SMB1-specific) in common code. See smbsr_status().
+ * If the log message below is seen, put a dtrace probe on this
+ * function with a stack() action to see who is calling the SMB1
+ * "put error" from common code, and fix it.
+ */
+void
+smbsr_status_smb2(smb_request_t *sr, DWORD status)
+{
+ const char *name;
+
+ if (sr->smb2_cmd_code < SMB2__NCMDS)
+ name = smb2_disp_table[sr->smb2_cmd_code].sdt_name;
+ else
+ name = "<unknown>";
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "smbsr_status called for %s", name);
+#endif
+
+ smb2sr_put_error_data(sr, status, NULL);
+}
+
+void
+smb2sr_put_errno(struct smb_request *sr, int errnum)
+{
+ uint32_t status = smb_errno2status(errnum);
+ smb2sr_put_error_data(sr, status, NULL);
+}
+
+void
+smb2sr_put_error(smb_request_t *sr, uint32_t status)
+{
+ smb2sr_put_error_data(sr, status, NULL);
+}
+
+/*
+ * Build an SMB2 error response. [MS-SMB2] 2.2.2
+ */
+void
+smb2sr_put_error_data(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc)
+{
+ DWORD len;
+
+ /*
+ * The common dispatch code writes this when it
+ * updates the SMB2 header before sending.
+ */
+ sr->smb2_status = status;
+
+ /* Rewind to the end of the SMB header. */
+ sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE;
+
+ /*
+ * NB: Must provide at least one byte of error data,
+ * per [MS-SMB2] 2.2.2
+ */
+ if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) {
+ (void) smb_mbc_encodef(
+ &sr->reply,
+ "wwlC",
+ 9, /* StructSize */ /* w */
+ 0, /* reserved */ /* w */
+ len, /* l */
+ mbc); /* C */
+ } else {
+ (void) smb_mbc_encodef(
+ &sr->reply,
+ "wwl.",
+ 9, /* StructSize */ /* w */
+ 0, /* reserved */ /* w */
+ 0); /* l. */
+ }
+}
+
+/*
+ * smb2sr_lookup_fid
+ *
+ * Setup sr->fid_ofile, either inherited from a related command,
+ * or obtained via FID lookup. Similar inheritance logic as in
+ * smb2sr_work.
+ */
+uint32_t
+smb2sr_lookup_fid(smb_request_t *sr, smb2fid_t *fid)
+{
+ boolean_t related = sr->smb2_hdr_flags &
+ SMB2_FLAGS_RELATED_OPERATIONS;
+
+ if (related) {
+ if (sr->fid_ofile == NULL)
+ return (NT_STATUS_INVALID_PARAMETER);
+ sr->smb_fid = sr->fid_ofile->f_fid;
+ return (0);
+ }
+
+ /*
+ * If we could be sure this is called only once per cmd,
+ * we could simply ASSERT(sr->fid_ofile == NULL) here.
+ * However, there are cases where it can be called again
+ * handling the same command, so let's tolerate that.
+ */
+ if (sr->fid_ofile == NULL) {
+ sr->smb_fid = (uint16_t)fid->temporal;
+ sr->fid_ofile = smb_ofile_lookup_by_fid(sr, sr->smb_fid);
+ }
+ if (sr->fid_ofile == NULL)
+ return (NT_STATUS_FILE_CLOSED);
+
+ return (0);
+}
+
+/*
+ * smb2_dispatch_stats_init
+ *
+ * Initializes dispatch statistics for SMB2.
+ * See also smb_dispatch_stats_init(), which fills in
+ * the lower part of the statistics array, from zero
+ * through SMB_COM_NUM;
+ */
+void
+smb2_dispatch_stats_init(smb_server_t *sv)
+{
+ smb_disp_stats_t *sds = sv->sv_disp_stats2;
+ smb_kstat_req_t *ksr;
+ int i;
+
+ ksr = ((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_reqs2;
+
+ for (i = 0; i < SMB2__NCMDS; i++, ksr++) {
+ smb_latency_init(&sds[i].sdt_lat);
+ (void) strlcpy(ksr->kr_name, smb2_disp_table[i].sdt_name,
+ sizeof (ksr->kr_name));
+ }
+}
+
+/*
+ * smb2_dispatch_stats_fini
+ *
+ * Frees and destroyes the resources used for statistics.
+ */
+void
+smb2_dispatch_stats_fini(smb_server_t *sv)
+{
+ smb_disp_stats_t *sds = sv->sv_disp_stats2;
+ int i;
+
+ for (i = 0; i < SMB2__NCMDS; i++)
+ smb_latency_destroy(&sds[i].sdt_lat);
+}
+
+void
+smb2_dispatch_stats_update(smb_server_t *sv,
+ smb_kstat_req_t *ksr, int first, int nreq)
+{
+ smb_disp_stats_t *sds = sv->sv_disp_stats2;
+ int i;
+ int last;
+
+ last = first + nreq - 1;
+
+ if ((first < SMB2__NCMDS) && (last < SMB2__NCMDS)) {
+ for (i = first; i <= last; i++, ksr++) {
+ ksr->kr_rxb = sds[i].sdt_rxb;
+ ksr->kr_txb = sds[i].sdt_txb;
+ mutex_enter(&sds[i].sdt_lat.ly_mutex);
+ ksr->kr_nreq = sds[i].sdt_lat.ly_a_nreq;
+ ksr->kr_sum = sds[i].sdt_lat.ly_a_sum;
+ ksr->kr_a_mean = sds[i].sdt_lat.ly_a_mean;
+ ksr->kr_a_stddev =
+ sds[i].sdt_lat.ly_a_stddev;
+ ksr->kr_d_mean = sds[i].sdt_lat.ly_d_mean;
+ ksr->kr_d_stddev =
+ sds[i].sdt_lat.ly_d_stddev;
+ sds[i].sdt_lat.ly_d_mean = 0;
+ sds[i].sdt_lat.ly_d_nreq = 0;
+ sds[i].sdt_lat.ly_d_stddev = 0;
+ sds[i].sdt_lat.ly_d_sum = 0;
+ mutex_exit(&sds[i].sdt_lat.ly_mutex);
+ }
+ }
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_echo.c b/usr/src/uts/common/fs/smbsrv/smb2_echo.c
new file mode 100644
index 0000000000..f82a6cbeb4
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_echo.c
@@ -0,0 +1,50 @@
+/*
+ * 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 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_ECHO
+ */
+
+#include <smbsrv/smb2_kproto.h>
+
+smb_sdrc_t
+smb2_echo(smb_request_t *sr)
+{
+ uint16_t StructSize;
+ uint16_t reserved;
+ int rc;
+
+ /*
+ * SMB2 Echo request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "ww",
+ &StructSize, /* w */
+ &reserved); /* w */
+ if (rc)
+ return (SDRC_ERROR);
+ if (StructSize != 4)
+ return (SDRC_ERROR);
+
+ /*
+ * SMB2 Echo reply
+ */
+ (void) smb_mbc_encodef(
+ &sr->reply, "wwl",
+ 4, /* StructSize */ /* w */
+ 0); /* reserved */ /* w */
+
+ return (SDRC_SUCCESS);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_flush.c b/usr/src/uts/common/fs/smbsrv/smb2_flush.c
new file mode 100644
index 0000000000..ecdf2fdcb8
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_flush.c
@@ -0,0 +1,72 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_FLUSH
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+
+smb_sdrc_t
+smb2_flush(smb_request_t *sr)
+{
+ smb_ofile_t *of = NULL;
+ uint16_t StructSize;
+ uint16_t reserved1;
+ uint32_t reserved2;
+ smb2fid_t smb2fid;
+ uint32_t status;
+ int rc = 0;
+
+ /*
+ * SMB2 Flush request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "wwlqq",
+ &StructSize, /* w */
+ &reserved1, /* w */
+ &reserved2, /* l */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal); /* q */
+ if (rc)
+ return (SDRC_ERROR);
+ if (StructSize != 24)
+ return (SDRC_ERROR);
+
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status) {
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+ }
+ of = sr->fid_ofile;
+
+ /*
+ * XXX - todo:
+ * Flush named pipe should drain writes.
+ */
+ if ((of->f_node->flags & NODE_FLAGS_WRITE_THROUGH) == 0)
+ (void) smb_fsop_commit(sr, of->f_cr, of->f_node);
+
+ /*
+ * SMB2 Flush reply
+ */
+ (void) smb_mbc_encodef(
+ &sr->reply, "wwl",
+ 4, /* StructSize */ /* w */
+ 0); /* reserved */ /* w */
+
+ return (SDRC_SUCCESS);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_ioctl.c b/usr/src/uts/common/fs/smbsrv/smb2_ioctl.c
new file mode 100644
index 0000000000..f4a9f9a948
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_ioctl.c
@@ -0,0 +1,264 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_IOCTL
+ * [MS-SMB2] 3.3.5.15
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/winioctl.h>
+
+struct smb2_ioctbl_ent {
+ uint32_t te_code;
+ uint32_t te_flags;
+ uint32_t (*te_func)(smb_request_t *, smb_fsctl_t *);
+};
+static struct smb2_ioctbl_ent smb2_ioc_tbl[];
+
+/* te_flags */
+#define ITF_IPC_ONLY 1
+#define ITF_NO_FID 2
+#define ITF_DISK_FID 4
+
+smb_sdrc_t
+smb2_ioctl(smb_request_t *sr)
+{
+ smb2fid_t smb2fid;
+ smb_fsctl_t fsctl;
+ mbuf_chain_t in_mbc;
+ struct smb2_ioctbl_ent *te;
+ uint32_t InputOffset;
+ uint32_t MaxInputResp;
+ uint32_t OutputOffset;
+ uint32_t Flags;
+ uint32_t status;
+ uint16_t StructSize;
+ int rc = 0;
+
+ bzero(&in_mbc, sizeof (in_mbc));
+
+ /*
+ * SMB2 Ioctl request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "w..lqqlllllll4.",
+ &StructSize, /* w */
+ /* reserved .. */
+ &fsctl.CtlCode, /* l */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal, /* q */
+ &InputOffset, /* l */
+ &fsctl.InputCount, /* l */
+ &MaxInputResp, /* l */
+ &OutputOffset, /* l */
+ &fsctl.OutputCount, /* l */
+ &fsctl.MaxOutputResp, /* l */
+ &Flags); /* l */
+ /* reserved2 4. */
+ if (rc || StructSize != 57)
+ return (SDRC_ERROR);
+
+ if (Flags != SMB2_0_IOCTL_IS_FSCTL) {
+ status = NT_STATUS_NOT_SUPPORTED;
+ goto errout;
+ }
+
+ for (te = smb2_ioc_tbl; te->te_code; te++) {
+ if (te->te_code == fsctl.CtlCode)
+ break;
+ }
+ if (te->te_code == 0) {
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "smb2_ioctl: unknown code 0x%x",
+ fsctl.CtlCode);
+#endif
+ status = NT_STATUS_NOT_SUPPORTED;
+ goto errout;
+ }
+
+ /*
+ * Some requests are only valid on IPC$
+ */
+ if ((te->te_flags & ITF_IPC_ONLY) != 0 &&
+ !STYPE_ISIPC(sr->tid_tree->t_res_type)) {
+ status = NT_STATUS_INVALID_DEVICE_REQUEST;
+ goto errout;
+ }
+
+ /*
+ * Note: some ioctl commands don't need a FID.
+ */
+ if (te->te_flags & ITF_NO_FID) {
+ if (smb2fid.temporal != ~0LL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+ } else {
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status) {
+ status = NT_STATUS_FILE_CLOSED;
+ goto errout;
+ }
+ }
+
+ /*
+ * Note: some ioctls require a "disk" fid.
+ */
+ if (te->te_flags & ITF_DISK_FID) {
+ if (sr->fid_ofile == NULL ||
+ !SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+ }
+
+ /*
+ * If there's an input buffer, setup a shadow.
+ */
+ if (fsctl.InputCount) {
+ if (InputOffset < (SMB2_HDR_SIZE + 56)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+ rc = MBC_SHADOW_CHAIN(&in_mbc, &sr->smb_data,
+ sr->smb2_cmd_hdr + InputOffset, fsctl.InputCount);
+ if (rc) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+ }
+ fsctl.in_mbc = &in_mbc;
+
+ /*
+ * If output is possible, setup the output mbuf_chain
+ */
+ if (fsctl.MaxOutputResp > smb2_max_trans)
+ fsctl.MaxOutputResp = smb2_max_trans;
+ sr->raw_data.max_bytes = fsctl.MaxOutputResp;
+ fsctl.out_mbc = &sr->raw_data;
+
+ /*
+ * Dispatch to the handler for CtlCode
+ */
+ status = (te->te_func)(sr, &fsctl);
+ if (status != 0) {
+ sr->smb2_status = status;
+ if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_ERROR)
+ goto errout;
+ /* Warnings like NT_STATUS_BUFFER_OVERFLOW are OK. */
+ }
+
+ fsctl.InputCount = 0;
+ InputOffset = SMB2_HDR_SIZE + 48;
+
+ fsctl.OutputCount = MBC_LENGTH(&sr->raw_data);
+ OutputOffset = (fsctl.OutputCount) ? InputOffset : 0;
+
+ /*
+ * SMB2 Ioctl reply
+ */
+ StructSize = 49;
+ rc = smb_mbc_encodef(
+ &sr->reply, "w..lqqlllll4.#C",
+ StructSize, /* w */
+ /* reserved .. */
+ fsctl.CtlCode, /* l */
+ smb2fid.persistent, /* q */
+ smb2fid.temporal, /* q */
+ InputOffset, /* l */
+ fsctl.InputCount, /* l */
+ OutputOffset, /* l */
+ fsctl.OutputCount, /* l */
+ Flags, /* l */
+ /* reserved2 4. */
+ fsctl.OutputCount, /* # */
+ &sr->raw_data); /* C */
+ return ((rc) ? SDRC_ERROR : SDRC_SUCCESS);
+
+errout:
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+}
+
+/* ARGSUSED */
+static uint32_t
+smb2_fsctl_notsup(smb_request_t *sr, smb_fsctl_t *fsctl)
+{
+ return (NT_STATUS_NOT_SUPPORTED);
+}
+
+static struct smb2_ioctbl_ent
+smb2_ioc_tbl[] = {
+
+ /*
+ * FILE_DEVICE_DFS (6)
+ */
+ { FSCTL_DFS_GET_REFERRALS,
+ ITF_IPC_ONLY | ITF_NO_FID, smb_dfs_get_referrals },
+ { FSCTL_DFS_GET_REFERRALS_EX,
+ ITF_IPC_ONLY | ITF_NO_FID, smb_dfs_get_referrals },
+
+ /*
+ * FILE_DEVICE_FILE_SYSTEM (9)
+ */
+ { FSCTL_SET_REPARSE_POINT, 0, smb2_fsctl_notsup },
+ { FSCTL_CREATE_OR_GET_OBJECT_ID, 0, smb2_fsctl_notsup },
+ { FSCTL_FILE_LEVEL_TRIM, 0, smb2_fsctl_notsup },
+
+ /*
+ * FILE_DEVICE_NAMED_PIPE (17)
+ */
+ { FSCTL_PIPE_PEEK,
+ ITF_IPC_ONLY, smb_opipe_fsctl },
+ { FSCTL_PIPE_TRANSCEIVE,
+ ITF_IPC_ONLY, smb_opipe_fsctl },
+ { FSCTL_PIPE_WAIT,
+ ITF_IPC_ONLY | ITF_NO_FID, smb_opipe_fsctl },
+
+ /*
+ * FILE_DEVICE_NETWORK_FILE_SYSTEM (20)
+ */
+ { FSCTL_SRV_ENUMERATE_SNAPSHOTS,
+ ITF_DISK_FID, smb_vss_enum_snapshots },
+ { FSCTL_SRV_REQUEST_RESUME_KEY, 0, smb2_fsctl_notsup },
+ { FSCTL_SRV_COPYCHUNK, 0, smb2_fsctl_notsup },
+ { 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_QUERY_NETWORK_INTERFACE_INFO,
+ ITF_NO_FID, smb2_fsctl_notsup },
+ { FSCTL_VALIDATE_NEGOTIATE_INFO,
+ ITF_NO_FID, smb2_fsctl_vneginfo },
+
+ /*
+ * End marker
+ */
+ { 0, 0, 0 }
+};
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_lock.c b/usr/src/uts/common/fs/smbsrv/smb2_lock.c
new file mode 100644
index 0000000000..7374dead59
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_lock.c
@@ -0,0 +1,293 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_LOCK
+ */
+
+#include <smbsrv/smb2_kproto.h>
+
+struct SMB2_LOCK_ELEMENT {
+ uint64_t Offset;
+ uint64_t Length;
+ uint32_t Flags;
+ uint32_t reserved;
+};
+
+static smb_sdrc_t smb2_lock_async(smb_request_t *);
+static uint32_t smb2_lock_exec(smb_request_t *, uint16_t);
+static uint32_t smb2_lock_elem(smb_request_t *, struct SMB2_LOCK_ELEMENT *);
+
+/*
+ * This is a somewhat arbitrary sanity limit on the length of the
+ * SMB2_LOCK_ELEMENT array. It usually has length one or two.
+ */
+int smb2_lock_max_elem = 1024;
+
+smb_sdrc_t
+smb2_lock(smb_request_t *sr)
+{
+ struct SMB2_LOCK_ELEMENT elem;
+ smb2fid_t smb2fid;
+ uint32_t save_offset;
+ uint32_t LockSequence;
+ uint32_t status;
+ uint16_t StructSize;
+ uint16_t LockCount;
+ uint16_t i;
+ boolean_t MayBlock = B_FALSE;
+ int rc = 0;
+
+ /*
+ * SMB2 Lock request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "wwlqq",
+ &StructSize, /* w */
+ &LockCount, /* w */
+ &LockSequence, /* l */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal); /* q */
+ if (rc || StructSize != 48)
+ return (SDRC_ERROR);
+
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status)
+ goto errout;
+ if (sr->fid_ofile->f_node == NULL || LockCount == 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+ if (LockCount > smb2_lock_max_elem) {
+ status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ goto errout;
+ }
+
+ /*
+ * Process the array of SMB2_LOCK_ELEMENT structs
+ * We do this twice. (it's always a short list)
+ * The first time, just validate the flags, and check
+ * if any of the locking request might need to block.
+ * The second time (either here, or in the async
+ * handler function) process the locks for real.
+ */
+ save_offset = sr->smb_data.chain_offset;
+ for (i = 0; i < LockCount; i++) {
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "qqll",
+ &elem.Offset, /* q */
+ &elem.Length, /* q */
+ &elem.Flags, /* l */
+ &elem.reserved); /* l */
+ if (rc) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+
+ /*
+ * Make sure the flags are valid;
+ * Find out if we might block.
+ */
+ switch (elem.Flags) {
+ case SMB2_LOCKFLAG_SHARED_LOCK:
+ case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
+ MayBlock = B_TRUE;
+ break;
+
+ /* BEGIN CSTYLED */
+ case SMB2_LOCKFLAG_SHARED_LOCK |
+ SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
+ case SMB2_LOCKFLAG_EXCLUSIVE_LOCK |
+ SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
+ case SMB2_LOCKFLAG_UNLOCK:
+ /* END CSTYLED */
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+ }
+
+ if (MayBlock) {
+ /*
+ * May need to block. "Go async".
+ */
+ status = smb2sr_go_async(sr, smb2_lock_async);
+ goto errout;
+ }
+
+ sr->smb_data.chain_offset = save_offset;
+ status = smb2_lock_exec(sr, LockCount);
+ if (status)
+ goto errout;
+
+ /*
+ * SMB2 Lock reply (sync)
+ */
+ StructSize = 4;
+ (void) smb_mbc_encodef(
+ &sr->reply, "w..",
+ StructSize); /* w */
+ /* reserved .. */
+ return (SDRC_SUCCESS);
+
+errout:
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+}
+
+static smb_sdrc_t
+smb2_lock_async(smb_request_t *sr)
+{
+ smb2fid_t smb2fid;
+ uint32_t LockSequence;
+ uint32_t status;
+ uint16_t StructSize;
+ uint16_t LockCount;
+ int rc = 0;
+
+ /*
+ * Decode the lock request again. It should all decode
+ * exactly the same as the first time we saw it. If not,
+ * report an "internal error".
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "wwlqq",
+ &StructSize, /* w */
+ &LockCount, /* w */
+ &LockSequence, /* l */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal); /* q */
+ if (rc || StructSize != 48)
+ return (SDRC_ERROR);
+
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status)
+ goto errout;
+ if (sr->fid_ofile->f_node == NULL || LockCount == 0) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto errout;
+ }
+
+ status = smb2_lock_exec(sr, LockCount);
+ if (status)
+ goto errout;
+
+ /*
+ * SMB2 Lock reply (async)
+ */
+ StructSize = 4;
+ (void) smb_mbc_encodef(
+ &sr->reply, "w..",
+ StructSize); /* w */
+ /* reserved .. */
+ return (SDRC_SUCCESS);
+
+errout:
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+}
+
+/*
+ * Execute the vector of locks. This is the common function called by
+ * either the sync or async code paths. We've already decoded this
+ * request once when we get here, so if there are any decode errors
+ * then it's some kind of internal error.
+ */
+static uint32_t
+smb2_lock_exec(smb_request_t *sr, uint16_t LockCount)
+{
+ struct SMB2_LOCK_ELEMENT elem;
+ uint32_t status = 0;
+ uint16_t i;
+ int rc;
+
+ /*
+ * On entry, out position in the input data should be
+ * after both the SMB2 header and the fixed part of
+ * the SMB Lock request header (24).
+ */
+ ASSERT(sr->smb_data.chain_offset ==
+ (sr->smb2_cmd_hdr + SMB2_HDR_SIZE + 24));
+
+ /*
+ * This is checked by our callers, but let's make sure.
+ */
+ ASSERT(sr->fid_ofile->f_node != NULL);
+
+ for (i = 0; i < LockCount; i++) {
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "qqll",
+ &elem.Offset, /* q */
+ &elem.Length, /* q */
+ &elem.Flags, /* l */
+ &elem.reserved); /* l */
+ if (rc) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+ status = smb2_lock_elem(sr, &elem);
+ if (status)
+ break;
+ }
+ return (status);
+}
+
+static uint32_t
+smb2_lock_elem(smb_request_t *sr, struct SMB2_LOCK_ELEMENT *elem)
+{
+ smb_node_t *node = sr->fid_ofile->f_node;
+ uint32_t status;
+ uint32_t ltype;
+ uint32_t timeout = 0;
+
+ switch (elem->Flags) {
+ case SMB2_LOCKFLAG_SHARED_LOCK:
+ timeout = UINT_MAX;
+ /* FALLTHROUGH */
+ case SMB2_LOCKFLAG_SHARED_LOCK | SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
+ ltype = SMB_LOCK_TYPE_READONLY;
+ status = smb_lock_range(sr,
+ elem->Offset, elem->Length,
+ timeout, ltype);
+ break;
+
+ case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
+ timeout = UINT_MAX;
+ /* FALLTHROUGH */
+ case SMB2_LOCKFLAG_EXCLUSIVE_LOCK | SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
+ ltype = SMB_LOCK_TYPE_READWRITE;
+ status = smb_lock_range(sr,
+ elem->Offset, elem->Length,
+ timeout, ltype);
+ break;
+
+ case SMB2_LOCKFLAG_UNLOCK:
+ status = smb_unlock_range(sr, node,
+ elem->Offset, elem->Length);
+ break;
+
+ /*
+ * We've already checked the flags previously, so any
+ * surprises here are some kind of internal error.
+ */
+ default:
+ status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+ return (status);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_logoff.c b/usr/src/uts/common/fs/smbsrv/smb2_logoff.c
new file mode 100644
index 0000000000..6b6f532897
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_logoff.c
@@ -0,0 +1,54 @@
+/*
+ * 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 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_LOGOFF
+ */
+
+#include <smbsrv/smb2_kproto.h>
+
+smb_sdrc_t
+smb2_logoff(smb_request_t *sr)
+{
+ uint16_t StructSize;
+ uint16_t reserved;
+ int rc;
+
+ /*
+ * SMB2 Logoff request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "ww",
+ &StructSize, /* w */
+ &reserved); /* w */
+ if (rc)
+ return (SDRC_ERROR);
+ if (StructSize != 4)
+ return (SDRC_ERROR);
+
+ if (sr->uid_user == NULL)
+ return (SDRC_ERROR);
+ smb_user_logoff(sr->uid_user);
+
+ /*
+ * SMB2 Logoff reply
+ */
+ (void) smb_mbc_encodef(
+ &sr->reply, "wwl",
+ 4, /* StructSize */ /* w */
+ 0); /* reserved */ /* w */
+
+ return (SDRC_SUCCESS);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c b/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c
new file mode 100644
index 0000000000..a0affa8a58
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c
@@ -0,0 +1,377 @@
+/*
+ * 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 2015 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_NEGOTIATE
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb2.h>
+
+static int smb2_negotiate_common(smb_request_t *, uint16_t);
+
+uint32_t smb2srv_capabilities =
+ SMB2_CAP_DFS |
+ SMB2_CAP_LARGE_MTU;
+
+/*
+ * These are not intended as customer tunables, but dev. & test folks
+ * might want to adjust them (with caution).
+ *
+ * smb2_tcp_bufsize is the TCP buffer size, applied to the network socket
+ * with setsockopt SO_SNDBUF, SO_RCVBUF. These set the TCP window size.
+ * This is also used as a "sanity limit" for internal send/reply message
+ * allocations. Note that with compounding SMB2 messages may contain
+ * multiple requests/responses. This size should be large enough for
+ * at least a few SMB2 requests, and at least 2X smb2_max_rwsize.
+ *
+ * smb2_max_rwsize is what we put in the SMB2 negotiate response to tell
+ * the client the largest read and write request size we'll support.
+ * One megabyte is a compromise between efficiency on fast networks
+ * and memory consumption (for the buffers) on the server side.
+ *
+ * smb2_max_trans is the largest "transact" send or receive, which is
+ * used for directory listings and info set/get operations.
+ */
+uint32_t smb2_tcp_bufsize = (1<<22); /* 4MB */
+uint32_t smb2_max_rwsize = (1<<20); /* 1MB */
+uint32_t smb2_max_trans = (1<<16); /* 64KB */
+
+/*
+ * List of all SMB2 versions we implement. Note that the
+ * highest version we support may be limited by the
+ * _cfg.skc_max_protocol setting.
+ */
+static uint16_t smb2_versions[] = {
+ 0x202, /* SMB 2.002 */
+ 0x210, /* SMB 2.1 */
+};
+static uint16_t smb2_nversions =
+ sizeof (smb2_versions) / sizeof (smb2_versions[0]);
+
+static boolean_t
+smb2_supported_version(smb_session_t *s, uint16_t version)
+{
+ int i;
+
+ if (version > s->s_cfg.skc_max_protocol)
+ return (B_FALSE);
+ for (i = 0; i < smb2_nversions; i++)
+ if (version == smb2_versions[i])
+ return (B_TRUE);
+ return (B_FALSE);
+}
+
+/*
+ * Helper for the (SMB1) smb_com_negotiate(). This is the
+ * very unusual protocol interaction where an SMB1 negotiate
+ * gets an SMB2 negotiate response. This is the normal way
+ * clients first find out if the server supports SMB2.
+ *
+ * Note: This sends an SMB2 reply _itself_ and then returns
+ * SDRC_NO_REPLY so the caller will not send an SMB1 reply.
+ * Also, this is called directly from the reader thread, so
+ * we know this is the only thread using this session.
+ *
+ * The caller frees this request.
+ */
+smb_sdrc_t
+smb1_negotiate_smb2(smb_request_t *sr)
+{
+ smb_session_t *s = sr->session;
+ smb_arg_negotiate_t *negprot = sr->sr_negprot;
+ uint16_t smb2_version;
+ uint16_t secmode2;
+ int rc;
+
+ /*
+ * Note: In the SMB1 negotiate command handler, we
+ * agreed with one of the SMB2 dialects. If that
+ * dialect was "SMB 2.002", we'll respond here with
+ * version 0x202 and negotiation is done. If that
+ * dialect was "SMB 2.???", we'll respond here with
+ * the "wildcard" version 0x2FF, and the client will
+ * come back with an SMB2 negotiate.
+ */
+ switch (negprot->ni_dialect) {
+ case DIALECT_SMB2002: /* SMB 2.002 (a.k.a. SMB2.0) */
+ smb2_version = 0x202;
+ s->dialect = smb2_version;
+ s->s_state = SMB_SESSION_STATE_NEGOTIATED;
+ /* Allow normal SMB2 requests now. */
+ s->newrq_func = smb2sr_newrq;
+
+ /*
+ * Translate SMB1 sec. mode to SMB2.
+ */
+ secmode2 = 0;
+ if (s->secmode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED)
+ secmode2 |= SMB2_NEGOTIATE_SIGNING_ENABLED;
+ if (s->secmode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED)
+ secmode2 |= SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ s->secmode = secmode2;
+ break;
+ case DIALECT_SMB2XXX: /* SMB 2.??? (wildcard vers) */
+ /*
+ * Expecting an SMB2 negotiate next, so keep the
+ * initial s->newrq_func. Note that secmode is
+ * fiction good enough to pass the signing check
+ * in smb2_negotiate_common(). We'll check the
+ * real secmode when the 2nd negotiate comes.
+ */
+ smb2_version = 0x2FF;
+ s->secmode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+ break;
+ default:
+ return (SDRC_DROP_VC);
+ }
+
+ /*
+ * We did not decode an SMB2 header, so make sure
+ * the SMB2 header fields are initialized.
+ * (Most are zero from smb_request_alloc.)
+ * Also, the SMB1 common dispatch code reserved space
+ * for an SMB1 header, which we need to undo here.
+ */
+ sr->smb2_reply_hdr = sr->reply.chain_offset = 0;
+ sr->smb2_cmd_code = SMB2_NEGOTIATE;
+
+ rc = smb2_negotiate_common(sr, smb2_version);
+ if (rc != 0)
+ return (SDRC_DROP_VC);
+
+ return (SDRC_NO_REPLY);
+}
+
+/*
+ * SMB2 Negotiate gets special handling. This is called directly by
+ * the reader thread (see smbsr_newrq_initial) with what _should_ be
+ * an SMB2 Negotiate. Only the "\feSMB" header has been checked
+ * when this is called, so this needs to check the SMB command,
+ * if it's Negotiate execute it, then send the reply, etc.
+ *
+ * Since this is called directly from the reader thread, we
+ * know this is the only thread currently using this session.
+ * This has to duplicate some of what smb2sr_work does as a
+ * result of bypassing the normal dispatch mechanism.
+ *
+ * The caller always frees this request.
+ */
+int
+smb2_newrq_negotiate(smb_request_t *sr)
+{
+ smb_session_t *s = sr->session;
+ int i, rc;
+ uint16_t struct_size;
+ uint16_t best_version;
+ uint16_t version_cnt;
+ uint16_t cl_versions[8];
+
+ sr->smb2_cmd_hdr = sr->command.chain_offset;
+ rc = smb2_decode_header(sr);
+ if (rc != 0)
+ return (rc);
+
+ if ((sr->smb2_cmd_code != SMB2_NEGOTIATE) ||
+ (sr->smb2_next_command != 0))
+ return (SDRC_DROP_VC);
+
+ /*
+ * Decode SMB2 Negotiate (fixed-size part)
+ */
+ rc = smb_mbc_decodef(
+ &sr->command, "www..l16.8.",
+ &struct_size, /* w */
+ &version_cnt, /* w */
+ &s->secmode, /* w */
+ /* reserved (..) */
+ &s->capabilities); /* l */
+ /* clnt_uuid 16. */
+ /* start_time 8. */
+ if (rc != 0)
+ return (rc);
+ if (struct_size != 36 || version_cnt > 8)
+ return (SDRC_DROP_VC);
+
+ /*
+ * Decode SMB2 Negotiate (variable part)
+ */
+ rc = smb_mbc_decodef(&sr->command,
+ "#w", version_cnt, cl_versions);
+ if (rc != 0)
+ return (SDRC_DROP_VC);
+
+ /*
+ * The client offers an array of protocol versions it
+ * supports, which we have decoded into cl_versions[].
+ * We walk the array and pick the highest supported.
+ */
+ best_version = 0;
+ for (i = 0; i < version_cnt; i++)
+ if (smb2_supported_version(s, cl_versions[i]) &&
+ best_version < cl_versions[i])
+ best_version = cl_versions[i];
+ if (best_version == 0)
+ return (SDRC_DROP_VC);
+ s->dialect = best_version;
+
+ /* Allow normal SMB2 requests now. */
+ s->s_state = SMB_SESSION_STATE_NEGOTIATED;
+ s->newrq_func = smb2sr_newrq;
+
+ rc = smb2_negotiate_common(sr, best_version);
+ if (rc != 0)
+ return (SDRC_DROP_VC);
+
+ return (0);
+}
+
+/*
+ * Common parts of SMB2 Negotiate, used for both the
+ * SMB1-to-SMB2 style, and straight SMB2 style.
+ * Do negotiation decisions, encode, send the reply.
+ */
+static int
+smb2_negotiate_common(smb_request_t *sr, uint16_t version)
+{
+ timestruc_t boot_tv, now_tv;
+ smb_session_t *s = sr->session;
+ int rc;
+ uint16_t secmode;
+
+ sr->smb2_status = 0;
+
+ /*
+ * Negotiation itself. First the Security Mode.
+ * The caller stashed the client's secmode in s->secmode,
+ * which we validate, and then replace with the server's
+ * secmode, which is all we care about after this.
+ */
+ secmode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+ if (sr->sr_cfg->skc_signing_required) {
+ secmode |= SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ /* Make sure client at least enables signing. */
+ if ((s->secmode & secmode) == 0) {
+ sr->smb2_status = NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ s->secmode = secmode;
+
+ s->cmd_max_bytes = smb2_tcp_bufsize;
+ s->reply_max_bytes = smb2_tcp_bufsize;
+
+ /*
+ * "The number of credits held by the client MUST be considered
+ * as 1 when the connection is established." [MS-SMB2]
+ * We leave credits at 1 until the first successful
+ * session setup is completed.
+ */
+ s->s_cur_credits = s->s_max_credits = 1;
+ sr->smb2_credit_response = 1;
+
+ boot_tv.tv_sec = smb_get_boottime();
+ boot_tv.tv_nsec = 0;
+ now_tv.tv_sec = gethrestime_sec();
+ now_tv.tv_nsec = 0;
+
+ /*
+ * SMB2 negotiate reply
+ */
+ sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR;
+ (void) smb2_encode_header(sr, B_FALSE);
+ if (sr->smb2_status != 0) {
+ smb2sr_put_error(sr, sr->smb2_status);
+ smb2_send_reply(sr);
+ return (-1); /* will drop */
+ }
+
+ rc = smb_mbc_encodef(
+ &sr->reply,
+ "wwww#cllllTTwwl#c",
+ 65, /* StructSize */ /* w */
+ s->secmode, /* w */
+ version, /* w */
+ 0, /* reserved */ /* w */
+ UUID_LEN, /* # */
+ &s->s_cfg.skc_machine_uuid, /* c */
+ smb2srv_capabilities, /* l */
+ smb2_max_trans, /* l */
+ smb2_max_rwsize, /* l */
+ smb2_max_rwsize, /* l */
+ &now_tv, /* T */
+ &boot_tv, /* T */
+ 128, /* SecBufOff */ /* w */
+ sr->sr_cfg->skc_negtok_len, /* w */
+ 0, /* reserved */ /* l */
+ sr->sr_cfg->skc_negtok_len, /* # */
+ sr->sr_cfg->skc_negtok); /* c */
+
+ smb2_send_reply(sr);
+
+ (void) ksocket_setsockopt(s->sock, SOL_SOCKET,
+ SO_SNDBUF, (const void *)&smb2_tcp_bufsize,
+ sizeof (smb2_tcp_bufsize), CRED());
+ (void) ksocket_setsockopt(s->sock, SOL_SOCKET,
+ SO_RCVBUF, (const void *)&smb2_tcp_bufsize,
+ sizeof (smb2_tcp_bufsize), CRED());
+
+ return (rc);
+}
+
+/*
+ * SMB2 Dispatch table handler, which will run if we see an
+ * SMB2_NEGOTIATE after the initial negotiation is done.
+ * That would be a protocol error.
+ */
+smb_sdrc_t
+smb2_negotiate(smb_request_t *sr)
+{
+ sr->smb2_status = NT_STATUS_INVALID_PARAMETER;
+ return (SDRC_ERROR);
+}
+
+/*
+ * VALIDATE_NEGOTIATE_INFO [MS-SMB2] 2.2.32.6
+ */
+uint32_t
+smb2_fsctl_vneginfo(smb_request_t *sr, smb_fsctl_t *fsctl)
+{
+ smb_session_t *s = sr->session;
+ int rc;
+
+ /*
+ * The spec. says to parse the VALIDATE_NEGOTIATE_INFO here
+ * and verify that the original negotiate was not modified.
+ * The only tampering we need worry about is secmode, and
+ * we're not taking that from the client, so don't bother.
+ *
+ * One interesting requirement here is that we MUST reply
+ * with exactly the same information as we returned in our
+ * original reply to the SMB2 negotiate on this session.
+ * If we don't the client closes the connection.
+ */
+
+ rc = smb_mbc_encodef(
+ fsctl->out_mbc, "l#cww",
+ smb2srv_capabilities, /* l */
+ UUID_LEN, /* # */
+ &s->s_cfg.skc_machine_uuid, /* c */
+ s->secmode, /* w */
+ s->dialect); /* w */
+ if (rc)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_ofile.c b/usr/src/uts/common/fs/smbsrv/smb2_ofile.c
new file mode 100644
index 0000000000..1c07d87dbe
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_ofile.c
@@ -0,0 +1,110 @@
+/*
+ * 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 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Helper functions for SMB2 open handles
+ */
+
+#include <smbsrv/smb2_kproto.h>
+
+uint32_t
+smb2_ofile_getattr(smb_request_t *sr, smb_ofile_t *of, smb_attr_t *ap)
+{
+ uint_t mask;
+ int rc;
+
+ mask = ap->sa_mask;
+ bzero(ap, sizeof (*ap));
+ ap->sa_mask = mask;
+
+ switch (of->f_ftype) {
+ case SMB_FTYPE_DISK:
+ case SMB_FTYPE_PRINTER:
+ rc = smb_node_getattr(sr, of->f_node, of->f_cr, of, ap);
+ break;
+ case SMB_FTYPE_BYTE_PIPE:
+ case SMB_FTYPE_MESG_PIPE:
+ rc = smb_opipe_getattr(of, ap);
+ break;
+ default:
+ rc = ENOTTY;
+ break;
+ }
+ if (rc)
+ return (smb_errno2status(rc));
+
+ return (0);
+}
+
+/*
+ * Get the stuff needed by FileStandardInformation that was
+ * not already obtained by smb2_ofile_getattr().
+ * (qi_delete_on_close, qi_isdir)
+ */
+uint32_t
+smb2_ofile_getstd(smb_ofile_t *of, smb_queryinfo_t *qi)
+{
+ smb_node_t *node;
+
+ switch (of->f_ftype) {
+ case SMB_FTYPE_DISK:
+ case SMB_FTYPE_PRINTER:
+ node = of->f_node;
+ qi->qi_delete_on_close =
+ (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) != 0;
+ qi->qi_isdir = smb_node_is_dir(node);
+ break;
+ case SMB_FTYPE_BYTE_PIPE:
+ case SMB_FTYPE_MESG_PIPE:
+ qi->qi_delete_on_close = 1;
+ qi->qi_isdir = 0;
+ break;
+ default:
+ return (NT_STATUS_INVALID_DEVICE_REQUEST);
+ }
+
+ return (0);
+}
+
+/*
+ * Get info for FileNameInformation, FileAlternateNameInformation.
+ * (qi_name, qi_shortname)
+ */
+uint32_t
+smb2_ofile_getname(smb_ofile_t *of, smb_queryinfo_t *qi)
+{
+ int rc;
+
+ switch (of->f_ftype) {
+ case SMB_FTYPE_DISK:
+ case SMB_FTYPE_PRINTER:
+ rc = smb_node_getshrpath(of->f_node, of->f_tree,
+ qi->qi_name, MAXPATHLEN);
+ break;
+ case SMB_FTYPE_BYTE_PIPE:
+ case SMB_FTYPE_MESG_PIPE:
+ rc = smb_opipe_getname(of, qi->qi_name, MAXPATHLEN);
+ break;
+ default:
+ rc = ENOTTY;
+ break;
+ }
+ if (rc)
+ return (smb_errno2status(rc));
+ qi->qi_namelen = smb_wcequiv_strlen(qi->qi_name);
+
+ return (0);
+
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_oplock.c b/usr/src/uts/common/fs/smbsrv/smb2_oplock.c
new file mode 100644
index 0000000000..9bfca57c05
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_oplock.c
@@ -0,0 +1,152 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_OPLOCK_BREAK
+ */
+
+#include <smbsrv/smb2_kproto.h>
+
+/*
+ * SMB2 Oplock Break Acknowledgement
+ * [MS-SMB2 2.2.24]
+ */
+smb_sdrc_t
+smb2_oplock_break_ack(smb_request_t *sr)
+{
+ smb_node_t *node;
+ smb2fid_t smb2fid;
+ uint32_t status;
+ uint16_t StructSize;
+ uint8_t OplockLevel;
+ uint8_t brk;
+ int rc = 0;
+
+ /*
+ * Decode the SMB2 Oplock Break Ack.
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "wb5.qq",
+ &StructSize, /* w */
+ &OplockLevel, /* b */
+ /* reserved 5. */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal); /* q */
+ if (rc || StructSize != 24)
+ return (SDRC_ERROR);
+
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status)
+ goto errout;
+ if ((node = sr->fid_ofile->f_node) == NULL) {
+ /* Not a regular file */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+
+ /*
+ * Process the oplock break ack. We only expect levels
+ * at or below the hightest break levels we send, which is
+ * currently SMB2_OPLOCK_LEVEL_II.
+ */
+ switch (OplockLevel) {
+ case SMB2_OPLOCK_LEVEL_NONE: /* 0x00 */
+ brk = SMB_OPLOCK_BREAK_TO_NONE;
+ break;
+
+ case SMB2_OPLOCK_LEVEL_II: /* 0x01 */
+ brk = SMB_OPLOCK_BREAK_TO_LEVEL_II;
+ break;
+
+ /* We don't break to these levels (yet). */
+ case SMB2_OPLOCK_LEVEL_EXCLUSIVE: /* 0x08 */
+ case SMB2_OPLOCK_LEVEL_BATCH: /* 0x09 */
+ case SMB2_OPLOCK_LEVEL_LEASE: /* 0xFF */
+ default: /* gcc -Wuninitialized */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+
+ smb_oplock_ack(node, sr->fid_ofile, brk);
+
+ /*
+ * Generate SMB2 Oplock Break response
+ * [MS-SMB2] 2.2.25
+ */
+ StructSize = 24;
+ (void) smb_mbc_encodef(
+ &sr->reply, "wb5.qq",
+ StructSize, /* w */
+ OplockLevel, /* b */
+ /* reserved 5. */
+ smb2fid.persistent, /* q */
+ smb2fid.temporal); /* q */
+ return (SDRC_SUCCESS);
+
+errout:
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+}
+
+/*
+ * Compose an SMB2 Oplock Break Notification packet, including
+ * the SMB2 header and everything, in sr->reply.
+ * The caller will send it and free the request.
+ */
+void
+smb2_oplock_break_notification(smb_request_t *sr, uint8_t brk)
+{
+ smb_ofile_t *ofile = sr->fid_ofile;
+ smb2fid_t smb2fid;
+ uint16_t StructSize;
+ uint8_t OplockLevel;
+
+ switch (brk) {
+ default:
+ ASSERT(0);
+ /* FALLTHROUGH */
+ case SMB_OPLOCK_BREAK_TO_NONE:
+ OplockLevel = SMB2_OPLOCK_LEVEL_NONE;
+ break;
+ case SMB_OPLOCK_BREAK_TO_LEVEL_II:
+ OplockLevel = SMB2_OPLOCK_LEVEL_II;
+ break;
+ }
+
+ /*
+ * SMB2 Header
+ */
+ 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_pid = 0;
+ sr->smb_uid = 0;
+ sr->smb2_messageid = UINT64_MAX;
+ (void) smb2_encode_header(sr, B_FALSE);
+
+ /*
+ * SMB2 Oplock Break, variable part
+ */
+ StructSize = 24;
+ smb2fid.persistent = 0;
+ smb2fid.temporal = ofile->f_fid;
+ (void) smb_mbc_encodef(
+ &sr->reply, "wb5.qq",
+ StructSize, /* w */
+ OplockLevel, /* b */
+ /* reserved 5. */
+ smb2fid.persistent, /* q */
+ smb2fid.temporal); /* q */
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c
new file mode 100644
index 0000000000..e4242c72e6
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c
@@ -0,0 +1,578 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_QUERY_INFO
+ *
+ * [MS-FSCC 2.4] If a file system does not support ...
+ * an Information Classs, NT_STATUS_INVALID_PARAMETER...
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <smbsrv/ntifs.h>
+
+static uint32_t smb2_qif_all(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_basic(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_standard(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_internal(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_ea_size(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_access(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_name(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_position(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_full_ea(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_mode(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_alignment(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_all(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_altname(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_stream(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_pipe(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_pipe_lcl(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_pipe_rem(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_compr(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_opens(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_tags(smb_request_t *, smb_queryinfo_t *);
+
+
+uint32_t
+smb2_qinfo_file(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ smb_ofile_t *of = sr->fid_ofile;
+ uint_t mask = 0;
+ boolean_t getstd = B_FALSE;
+ boolean_t getname = B_FALSE;
+ uint32_t status;
+
+ /*
+ * Which attributes do we need from the FS?
+ */
+ switch (qi->qi_InfoClass) {
+ case FileBasicInformation:
+ mask = SMB_AT_BASIC;
+ break;
+ case FileStandardInformation:
+ mask = SMB_AT_STANDARD;
+ getstd = B_TRUE;
+ break;
+ case FileInternalInformation:
+ mask = SMB_AT_NODEID;
+ break;
+ case FileAllInformation:
+ mask = SMB_AT_ALL;
+ getstd = B_TRUE;
+ getname = B_TRUE;
+ break;
+
+ case FileNameInformation:
+ getname = B_TRUE;
+ break;
+
+ case FileAlternateNameInformation:
+ mask = SMB_AT_NODEID;
+ getname = B_TRUE;
+ break;
+
+ case FileStreamInformation:
+ mask = SMB_AT_STANDARD;
+ break;
+
+ case FileCompressionInformation:
+ mask = SMB_AT_SIZE | SMB_AT_ALLOCSZ;
+ break;
+
+ case FileNetworkOpenInformation:
+ mask = SMB_AT_BASIC | SMB_AT_STANDARD;
+
+ default:
+ break;
+ }
+
+ qi->qi_attr.sa_mask = mask;
+ qi->qi_node = of->f_node;
+ if (mask & SMB_AT_ALL) {
+ status = smb2_ofile_getattr(sr, of, &qi->qi_attr);
+ if (status)
+ return (status);
+ }
+ if (getstd) {
+ status = smb2_ofile_getstd(of, qi);
+ if (status)
+ return (status);
+ }
+ if (getname) {
+ status = smb2_ofile_getname(of, qi);
+ if (status)
+ return (status);
+ }
+
+ switch (qi->qi_InfoClass) {
+ case FileBasicInformation:
+ status = smb2_qif_basic(sr, qi);
+ break;
+ case FileStandardInformation:
+ status = smb2_qif_standard(sr, qi);
+ break;
+ case FileInternalInformation:
+ status = smb2_qif_internal(sr, qi);
+ break;
+ case FileEaInformation:
+ status = smb2_qif_ea_size(sr, qi);
+ break;
+ case FileAccessInformation:
+ status = smb2_qif_access(sr, qi);
+ break;
+ case FileNameInformation:
+ status = smb2_qif_name(sr, qi);
+ break;
+ case FilePositionInformation:
+ status = smb2_qif_position(sr, qi);
+ break;
+ case FileFullEaInformation:
+ status = smb2_qif_full_ea(sr, qi);
+ break;
+ case FileModeInformation:
+ status = smb2_qif_mode(sr, qi);
+ break;
+ case FileAlignmentInformation:
+ status = smb2_qif_alignment(sr, qi);
+ break;
+ case FileAllInformation:
+ status = smb2_qif_all(sr, qi);
+ break;
+ case FileAlternateNameInformation:
+ status = smb2_qif_altname(sr, qi);
+ break;
+ case FileStreamInformation:
+ status = smb2_qif_stream(sr, qi);
+ break;
+ case FilePipeInformation:
+ status = smb2_qif_pipe(sr, qi);
+ break;
+ case FilePipeLocalInformation:
+ status = smb2_qif_pipe_lcl(sr, qi);
+ break;
+ case FilePipeRemoteInformation:
+ status = smb2_qif_pipe_rem(sr, qi);
+ break;
+ case FileCompressionInformation:
+ status = smb2_qif_compr(sr, qi);
+ break;
+ case FileNetworkOpenInformation:
+ status = smb2_qif_opens(sr, qi);
+ break;
+ case FileAttributeTagInformation:
+ status = smb2_qif_tags(sr, qi);
+ break;
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ return (status);
+}
+
+/*
+ * FileAllInformation
+ *
+ * This returns a concatenation of:
+ * FileBasicInformation
+ * FileStandardInformation
+ * FileInternalInformation
+ * FileEaInformation
+ * FilePositionInformation
+ * FileModeInformation
+ * FileAlignmentInformation
+ * FileNameInformation
+ */
+static uint32_t
+smb2_qif_all(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ uint32_t status;
+
+ status = smb2_qif_basic(sr, qi);
+ if (status)
+ return (status);
+ status = smb2_qif_standard(sr, qi);
+ if (status)
+ return (status);
+ status = smb2_qif_internal(sr, qi);
+ if (status)
+ return (status);
+ status = smb2_qif_ea_size(sr, qi);
+ if (status)
+ return (status);
+ status = smb2_qif_position(sr, qi);
+ if (status)
+ return (status);
+ status = smb2_qif_mode(sr, qi);
+ if (status)
+ return (status);
+ status = smb2_qif_alignment(sr, qi);
+ if (status)
+ return (status);
+ status = smb2_qif_name(sr, qi);
+ if (status)
+ return (status);
+
+ return (0);
+}
+
+/*
+ * FileBasicInformation
+ * See also:
+ * case SMB_QUERY_FILE_BASIC_INFO:
+ * case SMB_FILE_BASIC_INFORMATION:
+ */
+static uint32_t
+smb2_qif_basic(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ smb_attr_t *sa = &qi->qi_attr;
+
+ ASSERT((sa->sa_mask & SMB_AT_BASIC) == SMB_AT_BASIC);
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "TTTTll",
+ &sa->sa_crtime, /* T */
+ &sa->sa_vattr.va_atime, /* T */
+ &sa->sa_vattr.va_mtime, /* T */
+ &sa->sa_vattr.va_ctime, /* T */
+ sa->sa_dosattr, /* l */
+ 0); /* reserved */ /* l */
+
+ return (0);
+}
+
+/*
+ * FileStandardInformation
+ * See also:
+ * SMB_QUERY_FILE_STANDARD_INFO
+ * SMB_FILE_STANDARD_INFORMATION
+ */
+static uint32_t
+smb2_qif_standard(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ smb_attr_t *sa = &qi->qi_attr;
+
+ ASSERT((sa->sa_mask & SMB_AT_STANDARD) == SMB_AT_STANDARD);
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "qqlbbw",
+ sa->sa_allocsz, /* q */
+ sa->sa_vattr.va_size, /* q */
+ sa->sa_vattr.va_nlink, /* l */
+ qi->qi_delete_on_close, /* b */
+ qi->qi_isdir, /* b */
+ 0); /* reserved */ /* w */
+
+ return (0);
+}
+
+/*
+ * FileInternalInformation
+ * See also:
+ * SMB_FILE_INTERNAL_INFORMATION
+ */
+static uint32_t
+smb2_qif_internal(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ smb_attr_t *sa = &qi->qi_attr;
+
+ ASSERT((sa->sa_mask & SMB_AT_NODEID) == SMB_AT_NODEID);
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "q",
+ sa->sa_vattr.va_nodeid); /* q */
+
+ return (0);
+}
+
+/*
+ * FileEaInformation
+ * See also:
+ * SMB_QUERY_FILE_EA_INFO
+ * SMB_FILE_EA_INFORMATION
+ */
+static uint32_t
+smb2_qif_ea_size(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ _NOTE(ARGUNUSED(qi))
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "l", 0);
+
+ return (0);
+}
+
+/*
+ * FileFullEaInformation
+ * We could put EAs in a named stream...
+ */
+/* ARGSUSED */
+static uint32_t
+smb2_qif_full_ea(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ return (NT_STATUS_NO_EAS_ON_FILE);
+}
+
+/*
+ * FileAccessInformation
+ */
+static uint32_t
+smb2_qif_access(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ _NOTE(ARGUNUSED(qi))
+ smb_ofile_t *of = sr->fid_ofile;
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "l",
+ of->f_granted_access);
+
+ return (0);
+}
+
+/*
+ * FileNameInformation
+ * See also:
+ * SMB_QUERY_FILE_NAME_INFO
+ * SMB_FILE_NAME_INFORMATION
+ */
+static uint32_t
+smb2_qif_name(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+
+ ASSERT(qi->qi_namelen > 0);
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "llU",
+ 0, /* FileIndex (l) */
+ qi->qi_namelen, /* l */
+ qi->qi_name); /* U */
+
+ return (0);
+}
+
+/*
+ * FilePositionInformation
+ */
+static uint32_t
+smb2_qif_position(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ _NOTE(ARGUNUSED(qi))
+ smb_ofile_t *of = sr->fid_ofile;
+ uint64_t pos;
+
+ mutex_enter(&of->f_mutex);
+ pos = of->f_seek_pos;
+ mutex_exit(&of->f_mutex);
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "q", pos);
+
+ return (0);
+}
+
+/*
+ * FileModeInformation [MS-FSA 2.4.24]
+ * XXX: These mode flags are supposed to be on the open handle,
+ * XXX: or I think so. Not yet... (just put zero for now)
+ */
+static uint32_t
+smb2_qif_mode(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ _NOTE(ARGUNUSED(qi))
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "l", 0);
+
+ return (0);
+}
+
+/*
+ * FileAlignmentInformation
+ */
+static uint32_t
+smb2_qif_alignment(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ _NOTE(ARGUNUSED(qi))
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "l", 0);
+
+ return (0);
+}
+
+/*
+ * FileAlternateNameInformation
+ * See also:
+ * SMB_QUERY_FILE_ALT_NAME_INFO
+ * SMB_FILE_ALT_NAME_INFORMATION
+ */
+static uint32_t
+smb2_qif_altname(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ smb_ofile_t *of = sr->fid_ofile;
+
+ ASSERT(qi->qi_namelen > 0);
+ ASSERT(qi->qi_attr.sa_mask & SMB_AT_NODEID);
+
+ if (of->f_ftype != SMB_FTYPE_DISK)
+ return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ if ((of->f_tree->t_flags & SMB_TREE_SHORTNAMES) == 0)
+ return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ /* fill in qi->qi_shortname */
+ smb_query_shortname(of->f_node, qi);
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "%lU", sr,
+ smb_wcequiv_strlen(qi->qi_shortname),
+ qi->qi_shortname);
+
+ return (0);
+}
+
+/*
+ * FileStreamInformation
+ */
+static uint32_t
+smb2_qif_stream(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ smb_ofile_t *of = sr->fid_ofile;
+ smb_attr_t *attr = &qi->qi_attr;
+ uint32_t status;
+
+ ASSERT((attr->sa_mask & SMB_AT_STANDARD) == SMB_AT_STANDARD);
+ if (of->f_ftype != SMB_FTYPE_DISK) {
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "l", 0);
+ return (0);
+ }
+
+ status = smb_query_stream_info(sr, &sr->raw_data, qi);
+ return (status);
+}
+
+/*
+ * FilePipeInformation
+ */
+static uint32_t
+smb2_qif_pipe(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ _NOTE(ARGUNUSED(qi))
+ smb_ofile_t *of = sr->fid_ofile;
+ uint32_t pipe_mode;
+ uint32_t nonblock;
+
+ switch (of->f_ftype) {
+ case SMB_FTYPE_BYTE_PIPE:
+ pipe_mode = 0; /* FILE_PIPE_BYTE_STREAM_MODE */
+ break;
+ case SMB_FTYPE_MESG_PIPE:
+ pipe_mode = 1; /* FILE_PIPE_MESSAGE_MODE */
+ break;
+ case SMB_FTYPE_DISK:
+ case SMB_FTYPE_PRINTER:
+ default:
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+ nonblock = 0; /* XXX todo: Get this from the pipe handle. */
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "ll",
+ pipe_mode, nonblock);
+
+ return (0);
+}
+
+/*
+ * FilePipeLocalInformation
+ */
+/* ARGSUSED */
+static uint32_t
+smb2_qif_pipe_lcl(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ return (NT_STATUS_INVALID_PARAMETER); /* XXX todo */
+}
+
+/*
+ * FilePipeRemoteInformation
+ */
+/* ARGSUSED */
+static uint32_t
+smb2_qif_pipe_rem(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ return (NT_STATUS_INVALID_PARAMETER); /* XXX todo */
+}
+
+/*
+ * FileCompressionInformation
+ * XXX: For now, just say "not compressed".
+ */
+static uint32_t
+smb2_qif_compr(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ smb_attr_t *sa = &qi->qi_attr;
+ uint16_t CompressionFormat = 0; /* COMPRESSION_FORMAT_NONE */
+
+ ASSERT(sa->sa_mask & SMB_AT_SIZE);
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "qw6.",
+ sa->sa_vattr.va_size, /* q */
+ CompressionFormat); /* w */
+
+ return (0);
+}
+
+/*
+ * FileNetworkOpenInformation
+ */
+static uint32_t
+smb2_qif_opens(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ smb_attr_t *sa = &qi->qi_attr;
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "TTTTqqll",
+ &sa->sa_crtime, /* T */
+ &sa->sa_vattr.va_atime, /* T */
+ &sa->sa_vattr.va_mtime, /* T */
+ &sa->sa_vattr.va_ctime, /* T */
+ sa->sa_allocsz, /* q */
+ sa->sa_vattr.va_size, /* q */
+ sa->sa_dosattr, /* l */
+ 0); /* reserved */ /* l */
+
+ return (0);
+}
+
+/*
+ * FileAttributeTagInformation
+ *
+ * If dattr includes FILE_ATTRIBUTE_REPARSE_POINT, the
+ * second dword should be the reparse tag. Otherwise
+ * the tag value should be set to zero.
+ * We don't support reparse points, so we set the tag
+ * to zero.
+ */
+static uint32_t
+smb2_qif_tags(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ _NOTE(ARGUNUSED(qi))
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "ll", 0, 0);
+
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_qinfo_fs.c b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_fs.c
new file mode 100644
index 0000000000..bd1b37ed9d
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_fs.c
@@ -0,0 +1,289 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_QUERY_INFO
+ *
+ * [MS-FSCC 2.5] If a file system does not implement ...
+ * an Information Classs, NT_STATUS_INVALID_PARAMETER...
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <smbsrv/ntifs.h>
+
+uint32_t smb2_qfs_volume(smb_request_t *);
+uint32_t smb2_qfs_size(smb_request_t *);
+uint32_t smb2_qfs_device(smb_request_t *);
+uint32_t smb2_qfs_attr(smb_request_t *);
+uint32_t smb2_qfs_control(smb_request_t *);
+uint32_t smb2_qfs_fullsize(smb_request_t *);
+uint32_t smb2_qfs_obj_id(smb_request_t *);
+
+uint32_t
+smb2_qinfo_fs(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ uint32_t status;
+
+ switch (qi->qi_InfoClass) {
+
+ /* pg 153 */
+ case FileFsVolumeInformation: /* 1 */
+ status = smb2_qfs_volume(sr);
+ break;
+ case FileFsSizeInformation: /* 3 */
+ status = smb2_qfs_size(sr);
+ break;
+ case FileFsDeviceInformation: /* 4 */
+ status = smb2_qfs_device(sr);
+ break;
+ case FileFsAttributeInformation: /* 5 */
+ status = smb2_qfs_attr(sr);
+ break;
+ case FileFsControlInformation: /* 6 */
+ status = smb2_qfs_control(sr);
+ break;
+ case FileFsFullSizeInformation: /* 7 */
+ status = smb2_qfs_fullsize(sr);
+ break;
+ case FileFsObjectIdInformation: /* 8 */
+ status = smb2_qfs_obj_id(sr);
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ return (status);
+}
+
+/*
+ * FileFsVolumeInformation
+ */
+uint32_t
+smb2_qfs_volume(smb_request_t *sr)
+{
+ smb_tree_t *tree = sr->tid_tree;
+ smb_node_t *snode;
+ fsid_t fsid;
+ uint32_t LabelLength;
+
+ if (!STYPE_ISDSK(tree->t_res_type))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ snode = tree->t_snode;
+ fsid = SMB_NODE_FSID(snode);
+
+ LabelLength = smb_wcequiv_strlen(tree->t_volume);
+
+ /*
+ * NT has the "supports objects" flag set to 1.
+ */
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "qllb.U",
+ 0LL, /* Volume creation time (q) */
+ fsid.val[0], /* serial no. (l) */
+ LabelLength, /* (l) */
+ 0, /* Supports objects (b) */
+ /* reserved (.) */
+ tree->t_volume); /* (U) */
+
+ return (0);
+}
+
+/*
+ * FileFsSizeInformation
+ */
+uint32_t
+smb2_qfs_size(smb_request_t *sr)
+{
+ smb_fssize_t fssize;
+ smb_tree_t *tree = sr->tid_tree;
+ int rc;
+
+ if (!STYPE_ISDSK(tree->t_res_type))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ rc = smb_fssize(sr, &fssize);
+ if (rc)
+ return (smb_errno2status(rc));
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "qqll",
+ fssize.fs_caller_units,
+ fssize.fs_caller_avail,
+ fssize.fs_sectors_per_unit,
+ fssize.fs_bytes_per_sector);
+
+ return (0);
+}
+
+/*
+ * FileFsFullSizeInformation
+ */
+uint32_t
+smb2_qfs_fullsize(smb_request_t *sr)
+{
+ smb_fssize_t fssize;
+ smb_tree_t *tree = sr->tid_tree;
+ int rc;
+
+ if (!STYPE_ISDSK(tree->t_res_type))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ rc = smb_fssize(sr, &fssize);
+ if (rc)
+ return (smb_errno2status(rc));
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "qqqll",
+ fssize.fs_caller_units,
+ fssize.fs_caller_avail,
+ fssize.fs_volume_avail,
+ fssize.fs_sectors_per_unit,
+ fssize.fs_bytes_per_sector);
+
+ return (0);
+}
+
+/*
+ * FileFsDeviceInformation
+ */
+uint32_t
+smb2_qfs_device(smb_request_t *sr)
+{
+ smb_tree_t *tree = sr->tid_tree;
+ uint32_t DeviceType;
+ uint32_t Characteristics;
+
+ if (!STYPE_ISDSK(tree->t_res_type))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ DeviceType = FILE_DEVICE_DISK;
+ Characteristics = FILE_DEVICE_IS_MOUNTED;
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "ll",
+ DeviceType,
+ Characteristics);
+
+ return (0);
+}
+
+/*
+ * FileFsAttributeInformation
+ */
+uint32_t
+smb2_qfs_attr(smb_request_t *sr)
+{
+ smb_tree_t *tree = sr->tid_tree;
+ char *fsname;
+ uint32_t namelen;
+ uint32_t FsAttr;
+
+ /* This call is OK on all tree types. */
+ switch (tree->t_res_type & STYPE_MASK) {
+ case STYPE_IPC:
+ fsname = "PIPE";
+ break;
+ case STYPE_DISKTREE:
+ fsname = "NTFS"; /* A lie, but compatible... */
+ break;
+ case STYPE_PRINTQ:
+ case STYPE_DEVICE:
+ default: /* gcc -Wuninitialized */
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+ namelen = smb_wcequiv_strlen(fsname);
+
+ /*
+ * Todo: Store the FsAttributes in the tree object,
+ * then just return that directly here.
+ */
+ FsAttr = FILE_CASE_PRESERVED_NAMES;
+ if (tree->t_flags & SMB_TREE_UNICODE_ON_DISK)
+ FsAttr |= FILE_UNICODE_ON_DISK;
+ if (tree->t_flags & SMB_TREE_SUPPORTS_ACLS)
+ FsAttr |= FILE_PERSISTENT_ACLS;
+ if ((tree->t_flags & SMB_TREE_CASEINSENSITIVE) == 0)
+ FsAttr |= FILE_CASE_SENSITIVE_SEARCH;
+ if (tree->t_flags & SMB_TREE_STREAMS)
+ FsAttr |= FILE_NAMED_STREAMS;
+ if (tree->t_flags & SMB_TREE_QUOTA)
+ FsAttr |= FILE_VOLUME_QUOTAS;
+ if (tree->t_flags & SMB_TREE_SPARSE)
+ FsAttr |= FILE_SUPPORTS_SPARSE_FILES;
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "lllU",
+ FsAttr,
+ MAXNAMELEN-1,
+ namelen,
+ fsname);
+
+ return (0);
+}
+
+/*
+ * FileFsControlInformation
+ */
+uint32_t
+smb2_qfs_control(smb_request_t *sr)
+{
+ smb_tree_t *tree = sr->tid_tree;
+
+ if (!STYPE_ISDSK(tree->t_res_type))
+ return (NT_STATUS_INVALID_PARAMETER);
+ if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) {
+ /*
+ * Strange error per. [MS-FSCC 2.5.2]
+ * which means quotas not supported.
+ */
+ return (NT_STATUS_VOLUME_NOT_UPGRADED);
+ }
+
+ (void) smb_mbc_encodef(
+ &sr->raw_data, "qqqqqll",
+ 0, /* free space start filtering - MUST be 0 */
+ 0, /* free space threshold - MUST be 0 */
+ 0, /* free space stop filtering - MUST be 0 */
+ SMB_QUOTA_UNLIMITED, /* default quota threshold */
+ SMB_QUOTA_UNLIMITED, /* default quota limit */
+ FILE_VC_QUOTA_ENFORCE, /* fs control flag */
+ 0); /* pad bytes */
+
+ return (0);
+}
+
+/*
+ * FileFsObjectIdInformation
+ */
+/* ARGSUSED */
+uint32_t
+smb2_qfs_obj_id(smb_request_t *sr)
+{
+ return (NT_STATUS_INVALID_PARAMETER);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_qinfo_quota.c b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_quota.c
new file mode 100644
index 0000000000..da2b0176b8
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_quota.c
@@ -0,0 +1,121 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_QUERY_INFO
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <smbsrv/ntifs.h>
+
+uint32_t
+smb2_qinfo_quota(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ _NOTE(ARGUNUSED(qi))
+ uint8_t single, restart;
+ uint32_t sidlistlen, startsidlen, startsidoff;
+ smb_node_t *tnode;
+ smb_ofile_t *ofile = sr->fid_ofile;
+ smb_quota_query_t request;
+ smb_quota_response_t reply;
+ uint32_t status = NT_STATUS_SUCCESS;
+ int rc;
+
+ bzero(&request, sizeof (smb_quota_query_t));
+ bzero(&reply, sizeof (smb_quota_response_t));
+
+ if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA))
+ return (NT_STATUS_NOT_SUPPORTED);
+
+ if ((ofile->f_node == NULL) ||
+ (ofile->f_ftype != SMB_FTYPE_DISK))
+ return (NT_STATUS_NOT_SUPPORTED);
+
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "bb..lll",
+ &single, /* b */
+ &restart, /* b */
+ /* reserved .. */
+ &sidlistlen, /* l */
+ &startsidlen, /* l */
+ &startsidoff); /* l */
+ if (rc)
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ if ((sidlistlen != 0) && (startsidlen != 0))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+
+ tnode = sr->tid_tree->t_snode;
+ request.qq_root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+ if (smb_node_getmntpath(tnode, request.qq_root_path, MAXPATHLEN) != 0) {
+ kmem_free(request.qq_root_path, MAXPATHLEN);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (sidlistlen != 0)
+ request.qq_query_op = SMB_QUOTA_QUERY_SIDLIST;
+ else if (startsidlen != 0)
+ request.qq_query_op = SMB_QUOTA_QUERY_STARTSID;
+ else
+ request.qq_query_op = SMB_QUOTA_QUERY_ALL;
+
+ request.qq_single = single;
+ request.qq_restart = restart;
+ smb_quota_max_quota(&sr->raw_data, &request);
+
+ status = smb_quota_init_sids(&sr->smb_data, &request, ofile);
+
+ if (status == NT_STATUS_SUCCESS) {
+ if (smb_quota_query(sr->sr_server, &request, &reply) != 0) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ } else {
+ status = reply.qr_status;
+ if (status == NT_STATUS_SUCCESS) {
+ status = smb_quota_encode_quotas(
+ &sr->raw_data,
+ &request, &reply, ofile);
+ }
+ xdr_free(smb_quota_response_xdr, (char *)&reply);
+ }
+ }
+
+ kmem_free(request.qq_root_path, MAXPATHLEN);
+ smb_quota_free_sids(&request);
+
+ if (status != NT_STATUS_SUCCESS) {
+ if (status == NT_STATUS_NO_MORE_ENTRIES) {
+ smb_ofile_set_quota_resume(ofile, NULL);
+ smbsr_warn(sr, status, 0, 0);
+ status = NT_STATUS_SUCCESS;
+ } else {
+ smbsr_error(sr, status, 0, 0);
+ }
+ }
+
+ return (status);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_qinfo_sec.c b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_sec.c
new file mode 100644
index 0000000000..36ab5547b2
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_sec.c
@@ -0,0 +1,94 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_QUERY_INFO
+ * Similar to smb_nt_transact_security.c
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <smbsrv/ntifs.h>
+
+uint32_t
+smb2_qinfo_sec(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ smb_sd_t sd;
+ uint32_t secinfo = qi->qi_AddlInfo;
+ uint32_t sdlen;
+ uint32_t status;
+
+ /*
+ * secinfo & ...
+ * OWNER_SECURITY_INFORMATION,
+ * GROUP_SECURITY_INFORMATION,
+ * DACL_SECURITY_INFORMATION, ...
+ */
+
+ if ((sr->fid_ofile->f_node == NULL) ||
+ (sr->fid_ofile->f_ftype != SMB_FTYPE_DISK))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ if (sr->tid_tree->t_acltype != ACE_T) {
+ /*
+ * If target filesystem doesn't support ACE_T acls then
+ * don't process SACL
+ */
+ secinfo &= ~SMB_SACL_SECINFO;
+ }
+
+ status = smb_sd_read(sr, &sd, secinfo);
+ if (status != NT_STATUS_SUCCESS)
+ return (status);
+
+ sdlen = smb_sd_len(&sd, secinfo);
+ if (sdlen == 0) {
+ status = NT_STATUS_INVALID_SECURITY_DESCR;
+ goto out;
+ }
+
+ if (sdlen > sr->raw_data.max_bytes) {
+ /*
+ * The maximum data return count specified by the
+ * client is not big enough to hold the security
+ * descriptor. Return the special error that
+ * tells the client how much room they need.
+ * Error data is the required size.
+ */
+ MBC_FLUSH(&sr->raw_data);
+ sr->raw_data.max_bytes = 4;
+ (void) smb_mbc_encodef(&sr->raw_data, "l", sdlen);
+ status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto out;
+ }
+
+ smb_encode_sd(&sr->raw_data, &sd, secinfo);
+ status = 0;
+
+out:
+ smb_sd_term(&sd);
+ return (status);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_query_dir.c b/usr/src/uts/common/fs/smbsrv/smb2_query_dir.c
new file mode 100644
index 0000000000..03d4382862
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_query_dir.c
@@ -0,0 +1,567 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_QUERY_DIRECTORY
+ *
+ * Similar to smb_trans2_find.c (from SMB1)
+ */
+
+#include <smbsrv/smb2_kproto.h>
+
+/*
+ * Args (and other state) that we carry around among the
+ * various functions involved in SMB2 Query Directory.
+ */
+typedef struct smb2_find_args {
+ uint32_t fa_maxdata;
+ uint8_t fa_infoclass;
+ uint8_t fa_fflags;
+ uint16_t fa_maxcount;
+ uint16_t fa_eos; /* End Of Search */
+ uint16_t fa_fixedsize; /* size of fixed part of a returned entry */
+ uint32_t fa_lastkey; /* Last resume key */
+ int fa_last_entry; /* offset of last entry */
+} smb2_find_args_t;
+
+static uint32_t smb2_find_entries(smb_request_t *,
+ smb_odir_t *, smb2_find_args_t *);
+static uint32_t smb2_find_mbc_encode(smb_request_t *,
+ smb_fileinfo_t *, smb2_find_args_t *);
+
+/*
+ * Tunable parameter to limit the maximum
+ * number of entries to be returned.
+ */
+uint16_t smb2_find_max = 128;
+
+smb_sdrc_t
+smb2_query_dir(smb_request_t *sr)
+{
+ smb2_find_args_t args;
+ smb_odir_resume_t odir_resume;
+ smb_ofile_t *of = NULL;
+ smb_odir_t *od = NULL;
+ char *pattern = NULL;
+ uint16_t StructSize;
+ uint32_t FileIndex;
+ uint16_t NameOffset;
+ uint16_t NameLength;
+ smb2fid_t smb2fid;
+ uint16_t sattr = SMB_SEARCH_ATTRIBUTES;
+ uint16_t DataOff;
+ uint32_t DataLen;
+ uint32_t status;
+ int skip, rc = 0;
+
+ bzero(&args, sizeof (args));
+ bzero(&odir_resume, sizeof (odir_resume));
+
+ /*
+ * SMB2 Query Directory request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "wbblqqwwl",
+ &StructSize, /* w */
+ &args.fa_infoclass, /* b */
+ &args.fa_fflags, /* b */
+ &FileIndex, /* l */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal, /* q */
+ &NameOffset, /* w */
+ &NameLength, /* w */
+ &args.fa_maxdata); /* l */
+ if (rc || StructSize != 33)
+ return (SDRC_ERROR);
+
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status)
+ goto errout;
+ of = sr->fid_ofile;
+
+ /*
+ * If there's an input buffer (search pattern), decode it.
+ * Two times MAXNAMELEN because it represents the UNICODE string
+ * length in bytes.
+ */
+ if (NameLength >= (2 * MAXNAMELEN)) {
+ status = NT_STATUS_OBJECT_PATH_INVALID;
+ goto errout;
+ }
+ if (NameLength != 0) {
+ /*
+ * We're normally positioned at the pattern now,
+ * but there could be some padding before it.
+ */
+ skip = (sr->smb2_cmd_hdr + NameOffset) -
+ sr->smb_data.chain_offset;
+ if (skip < 0) {
+ status = NT_STATUS_OBJECT_PATH_INVALID;
+ goto errout;
+ }
+ if (skip > 0)
+ (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
+ rc = smb_mbc_decodef(&sr->smb_data, "%#U", sr,
+ NameLength, &pattern);
+ if (rc || pattern == NULL) {
+ status = NT_STATUS_OBJECT_PATH_INVALID;
+ goto errout;
+ }
+ } else
+ pattern = "*";
+
+ /*
+ * Setup the output buffer.
+ */
+ if (args.fa_maxdata > smb2_max_trans)
+ args.fa_maxdata = smb2_max_trans;
+ sr->raw_data.max_bytes = args.fa_maxdata;
+
+ /*
+ * Get the mininum size of entries we will return, which
+ * lets us estimate the number of entries we'll need.
+ * This should be the size with a one character name.
+ * Compare w/ smb2_find_get_maxdata().
+ *
+ * Also use this opportunity to validate fa_infoclass.
+ */
+
+ switch (args.fa_infoclass) {
+ case FileDirectoryInformation: /* 1 */
+ args.fa_fixedsize = 64;
+ break;
+ case FileFullDirectoryInformation: /* 2 */
+ args.fa_fixedsize = 68;
+ break;
+ case FileBothDirectoryInformation: /* 3 */
+ args.fa_fixedsize = 94;
+ break;
+ case FileNamesInformation: /* 12 */
+ args.fa_fixedsize = 12;
+ break;
+ case FileIdBothDirectoryInformation: /* 37 */
+ args.fa_fixedsize = 96;
+ break;
+ case FileIdFullDirectoryInformation: /* 38 */
+ args.fa_fixedsize = 84;
+ break;
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ goto errout;
+ }
+
+ args.fa_maxcount = args.fa_maxdata / (args.fa_fixedsize + 4);
+ if (args.fa_maxcount == 0)
+ args.fa_maxcount = 1;
+ if ((smb2_find_max != 0) && (args.fa_maxcount > smb2_find_max))
+ args.fa_maxcount = smb2_find_max;
+ if (args.fa_fflags & SMB2_QDIR_FLAG_SINGLE)
+ args.fa_maxcount = 1;
+
+ /*
+ * If this ofile does not have an odir yet, get one.
+ */
+ mutex_enter(&of->f_mutex);
+ if ((od = of->f_odir) == NULL) {
+ status = smb_odir_openfh(sr, pattern, sattr, &od);
+ of->f_odir = od;
+ }
+ mutex_exit(&of->f_mutex);
+ if (od == NULL) {
+ if (status == 0)
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto errout;
+ }
+
+ /*
+ * "Reopen" sets a new pattern and restart.
+ */
+ if (args.fa_fflags & SMB2_QDIR_FLAG_REOPEN) {
+ smb_odir_reopen(od, pattern, sattr);
+ }
+
+ /*
+ * Set the correct position in the directory.
+ */
+ if (args.fa_fflags & SMB2_QDIR_FLAG_RESTART) {
+ odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
+ odir_resume.or_cookie = 0;
+ } else if (args.fa_fflags & SMB2_QDIR_FLAG_INDEX) {
+ odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
+ odir_resume.or_cookie = FileIndex;
+ } else {
+ odir_resume.or_type = SMB_ODIR_RESUME_CONT;
+ }
+ smb_odir_resume_at(od, &odir_resume);
+ of->f_seek_pos = od->d_offset;
+
+ /*
+ * The real work of readdir and format conversion.
+ */
+ status = smb2_find_entries(sr, od, &args);
+
+ of->f_seek_pos = od->d_offset;
+
+ if (status == NT_STATUS_NO_MORE_FILES) {
+ if (args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) {
+ status = NT_STATUS_NO_SUCH_FILE;
+ goto errout;
+ }
+ /*
+ * This is not an error, but a warning that can be
+ * used to tell the client that this data return
+ * is the last of the enumeration. Returning this
+ * warning now (with the data) saves the client a
+ * round trip that would otherwise be needed to
+ * find out it's at the end.
+ */
+ sr->smb2_status = status;
+ status = 0;
+ }
+ if (status)
+ goto errout;
+
+ /*
+ * SMB2 Query Directory reply
+ */
+ StructSize = 9;
+ DataOff = SMB2_HDR_SIZE + 8;
+ DataLen = MBC_LENGTH(&sr->raw_data);
+ rc = smb_mbc_encodef(
+ &sr->reply, "wwlC",
+ StructSize, /* w */
+ DataOff, /* w */
+ DataLen, /* l */
+ &sr->raw_data); /* C */
+ if (DataLen == 0)
+ (void) smb_mbc_encodef(&sr->reply, ".");
+ if (rc == 0)
+ return (SDRC_SUCCESS);
+ status = NT_STATUS_UNSUCCESSFUL;
+
+errout:
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+}
+
+/*
+ * smb2_find_entries
+ *
+ * Find and encode up to args->fa_maxcount directory entries.
+ *
+ * Returns:
+ * NT status
+ */
+static uint32_t
+smb2_find_entries(smb_request_t *sr, smb_odir_t *od, smb2_find_args_t *args)
+{
+ smb_fileinfo_t fileinfo;
+ smb_odir_resume_t odir_resume;
+ uint16_t count;
+ uint16_t minsize;
+ uint32_t status = 0;
+ int rc = -1;
+
+ /*
+ * Let's stop when the remaining space will not hold a
+ * minimum size entry. That's the fixed part plus the
+ * storage size of a 1 char unicode string.
+ */
+ minsize = args->fa_fixedsize + 2;
+
+ count = 0;
+ while (count < args->fa_maxcount) {
+
+ if (!MBC_ROOM_FOR(&sr->raw_data, minsize)) {
+ status = NT_STATUS_BUFFER_OVERFLOW;
+ break;
+ }
+
+ rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
+ if (rc == ENOENT) {
+ status = NT_STATUS_NO_MORE_FILES;
+ break;
+ }
+ if (rc != 0) {
+ status = smb_errno2status(rc);
+ break;
+ }
+ if (args->fa_eos != 0) {
+ /* The readdir call hit the end. */
+ status = NT_STATUS_NO_MORE_FILES;
+ break;
+ }
+
+ status = smb2_find_mbc_encode(sr, &fileinfo, args);
+ if (status) {
+ /*
+ * We read a directory entry but failed to
+ * copy it into the output buffer. Rewind
+ * the directory pointer so this will be
+ * the first entry read next time.
+ */
+ bzero(&odir_resume, sizeof (odir_resume));
+ odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
+ odir_resume.or_cookie = args->fa_lastkey;
+ smb_odir_resume_at(od, &odir_resume);
+ break;
+ }
+
+ /*
+ * Save the offset of the next entry we'll read.
+ * If we fail copying, we'll need this offset.
+ */
+ args->fa_lastkey = fileinfo.fi_cookie;
+ ++count;
+ }
+
+ if (count == 0) {
+ ASSERT(status != 0);
+ } else {
+ /*
+ * We copied some directory entries, but stopped for
+ * NT_STATUS_NO_MORE_FILES, or something.
+ *
+ * Per [MS-FSCC] sec. 2.4, the last entry in the
+ * enumeration MUST have its NextEntryOffset value
+ * set to zero. Overwrite that in the last entry.
+ */
+ (void) smb_mbc_poke(&sr->raw_data,
+ args->fa_last_entry, "l", 0);
+ status = 0;
+ }
+
+ return (status);
+}
+
+/*
+ * smb2_mbc_encode
+ *
+ * This function encodes the mbc for one directory entry.
+ *
+ * The function returns -1 when the max data requested by client
+ * is reached. If the entry is valid and successful encoded, 0
+ * will be returned; otherwise, 1 will be returned.
+ *
+ * We always null terminate the filename. The space for the null
+ * is included in the maxdata calculation and is therefore included
+ * in the next_entry_offset. namelen is the unterminated length of
+ * the filename. For levels except STANDARD and EA_SIZE, if the
+ * filename is ascii the name length returned to the client should
+ * include the null terminator. Otherwise the length returned to
+ * the client should not include the terminator.
+ *
+ * Returns: 0 - data successfully encoded
+ * NT status
+ */
+static uint32_t
+smb2_find_mbc_encode(smb_request_t *sr, smb_fileinfo_t *fileinfo,
+ smb2_find_args_t *args)
+{
+ uint8_t buf83[26];
+ smb_msgbuf_t mb;
+ int namelen, padsz;
+ int shortlen = 0;
+ int rc, starting_offset;
+ uint32_t next_entry_offset;
+ uint32_t mb_flags = SMB_MSGBUF_UNICODE;
+ uint32_t resume_key;
+
+ namelen = smb_wcequiv_strlen(fileinfo->fi_name);
+ if (namelen == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ /*
+ * Keep track of where the last entry starts so we can
+ * come back and poke the NextEntryOffset field. Also,
+ * after enumeration finishes, the caller uses this to
+ * poke the last entry again with zero to mark it as
+ * the end of the enumeration.
+ */
+ starting_offset = sr->raw_data.chain_offset;
+
+ /*
+ * Technically (per MS-SMB2) resume keys are optional.
+ * Windows doesn't need them, but MacOS does.
+ */
+ resume_key = fileinfo->fi_cookie;
+
+ /*
+ * This switch handles all the "information levels" (formats)
+ * that we support. Note that all formats have the file name
+ * placed after some fixed-size data, and the code to write
+ * the file name is factored out at the end of this switch.
+ */
+ switch (args->fa_infoclass) {
+
+ /* See also: SMB_FIND_FILE_DIRECTORY_INFO */
+ case FileDirectoryInformation: /* 1 */
+ rc = smb_mbc_encodef(
+ &sr->raw_data, "llTTTTqqll",
+ 0, /* NextEntryOffset (set later) */
+ resume_key,
+ &fileinfo->fi_crtime,
+ &fileinfo->fi_atime,
+ &fileinfo->fi_mtime,
+ &fileinfo->fi_ctime,
+ fileinfo->fi_size,
+ fileinfo->fi_alloc_size,
+ fileinfo->fi_dosattr,
+ namelen);
+ break;
+
+ /* See also: SMB_FIND_FILE_FULL_DIRECTORY_INFO */
+ case FileFullDirectoryInformation: /* 2 */
+ rc = smb_mbc_encodef(
+ &sr->raw_data, "llTTTTqqlll",
+ 0, /* NextEntryOffset (set later) */
+ resume_key,
+ &fileinfo->fi_crtime,
+ &fileinfo->fi_atime,
+ &fileinfo->fi_mtime,
+ &fileinfo->fi_ctime,
+ fileinfo->fi_size,
+ fileinfo->fi_alloc_size,
+ fileinfo->fi_dosattr,
+ namelen,
+ 0L); /* EaSize */
+ break;
+
+ /* See also: SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO */
+ case FileIdFullDirectoryInformation: /* 38 */
+ rc = smb_mbc_encodef(
+ &sr->raw_data, "llTTTTqqllllq",
+ 0, /* NextEntryOffset (set later) */
+ resume_key,
+ &fileinfo->fi_crtime,
+ &fileinfo->fi_atime,
+ &fileinfo->fi_mtime,
+ &fileinfo->fi_ctime,
+ fileinfo->fi_size,
+ fileinfo->fi_alloc_size,
+ fileinfo->fi_dosattr,
+ namelen,
+ 0L, /* EaSize */
+ 0L, /* reserved */
+ fileinfo->fi_nodeid);
+ break;
+
+ /* See also: SMB_FIND_FILE_BOTH_DIRECTORY_INFO */
+ case FileBothDirectoryInformation: /* 3 */
+ bzero(buf83, sizeof (buf83));
+ smb_msgbuf_init(&mb, buf83, sizeof (buf83), mb_flags);
+ if (!smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname))
+ shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname);
+
+ rc = smb_mbc_encodef(
+ &sr->raw_data, "llTTTTqqlllb.24c",
+ 0, /* NextEntryOffset (set later) */
+ resume_key,
+ &fileinfo->fi_crtime,
+ &fileinfo->fi_atime,
+ &fileinfo->fi_mtime,
+ &fileinfo->fi_ctime,
+ fileinfo->fi_size,
+ fileinfo->fi_alloc_size,
+ fileinfo->fi_dosattr,
+ namelen,
+ 0L, /* EaSize */
+ shortlen,
+ buf83);
+
+ smb_msgbuf_term(&mb);
+ break;
+
+ /* See also: SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO */
+ case FileIdBothDirectoryInformation: /* 37 */
+ bzero(buf83, sizeof (buf83));
+ smb_msgbuf_init(&mb, buf83, sizeof (buf83), mb_flags);
+ if (!smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname))
+ shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname);
+
+ rc = smb_mbc_encodef(
+ &sr->raw_data, "llTTTTqqlllb.24c..q",
+ 0, /* NextEntryOffset (set later) */
+ resume_key,
+ &fileinfo->fi_crtime,
+ &fileinfo->fi_atime,
+ &fileinfo->fi_mtime,
+ &fileinfo->fi_ctime,
+ fileinfo->fi_size, /* q */
+ fileinfo->fi_alloc_size, /* q */
+ fileinfo->fi_dosattr, /* l */
+ namelen, /* l */
+ 0L, /* EaSize l */
+ shortlen, /* b. */
+ buf83, /* 24c */
+ /* reserved .. */
+ fileinfo->fi_nodeid); /* q */
+
+ smb_msgbuf_term(&mb);
+ break;
+
+ /* See also: SMB_FIND_FILE_NAMES_INFO */
+ case FileNamesInformation: /* 12 */
+ rc = smb_mbc_encodef(
+ &sr->raw_data, "lll",
+ 0, /* NextEntryOffset (set later) */
+ resume_key,
+ namelen);
+ break;
+
+ default:
+ return (NT_STATUS_INVALID_INFO_CLASS);
+ }
+ if (rc) /* smb_mbc_encodef failed */
+ return (NT_STATUS_BUFFER_OVERFLOW);
+
+ /*
+ * At this point we have written all the fixed-size data
+ * for the specified info. class. Now put the name and
+ * alignment padding, and then patch the NextEntryOffset.
+ * Also store this offset for the caller so they can
+ * patch this (again) to zero on the very last entry.
+ */
+ rc = smb_mbc_encodef(
+ &sr->raw_data, "U",
+ fileinfo->fi_name);
+ if (rc)
+ return (NT_STATUS_BUFFER_OVERFLOW);
+
+ /* Next entry needs to be 8-byte aligned. */
+ padsz = sr->raw_data.chain_offset & 7;
+ if (padsz) {
+ padsz = 8 - padsz;
+ (void) smb_mbc_encodef(&sr->raw_data, "#.", padsz);
+ }
+ next_entry_offset = sr->raw_data.chain_offset - starting_offset;
+ (void) smb_mbc_poke(&sr->raw_data, starting_offset, "l",
+ next_entry_offset);
+ args->fa_last_entry = starting_offset;
+
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_query_info.c b/usr/src/uts/common/fs/smbsrv/smb2_query_info.c
new file mode 100644
index 0000000000..c39bce142c
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_query_info.c
@@ -0,0 +1,147 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_QUERY_INFO
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <smbsrv/ntifs.h>
+
+smb_sdrc_t
+smb2_query_info(smb_request_t *sr)
+{
+ smb_queryinfo_t *qi;
+ uint16_t StructSize;
+ uint32_t oBufLength;
+ uint16_t iBufOffset;
+ uint32_t iBufLength;
+ smb2fid_t smb2fid;
+ uint16_t DataOff;
+ uint32_t status;
+ smb_sdrc_t sdrc = SDRC_SUCCESS;
+ int rc = 0;
+
+ qi = kmem_zalloc(sizeof (*qi), KM_SLEEP);
+
+ /*
+ * SMB2 Query Info request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "wbblw..lllqq",
+ &StructSize, /* w */
+ &qi->qi_InfoType, /* b */
+ &qi->qi_InfoClass, /* b */
+ &oBufLength, /* l */
+ &iBufOffset, /* w */
+ /* reserved .. */
+ &iBufLength, /* l */
+ &qi->qi_AddlInfo, /* l */
+ &qi->qi_Flags, /* l */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal); /* q */
+ if (rc || StructSize != 41) {
+ sdrc = SDRC_ERROR;
+ goto out;
+ }
+
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status) {
+ smb2sr_put_error(sr, status);
+ goto out;
+ }
+
+ if (oBufLength > smb2_max_trans)
+ oBufLength = smb2_max_trans;
+
+ /*
+ * If there's an input buffer, setup a shadow.
+ */
+ if (iBufLength) {
+ rc = MBC_SHADOW_CHAIN(&qi->in_data, &sr->smb_data,
+ sr->smb2_cmd_hdr + iBufOffset, iBufLength);
+ if (rc) {
+ smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+ }
+
+ sr->raw_data.max_bytes = oBufLength;
+
+ switch (qi->qi_InfoType) {
+ case SMB2_0_INFO_FILE:
+ status = smb2_qinfo_file(sr, qi);
+ break;
+ case SMB2_0_INFO_FILESYSTEM:
+ status = smb2_qinfo_fs(sr, qi);
+ break;
+ case SMB2_0_INFO_SECURITY:
+ status = smb2_qinfo_sec(sr, qi);
+ break;
+ case SMB2_0_INFO_QUOTA:
+ status = smb2_qinfo_quota(sr, qi);
+ break;
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ switch (status) {
+
+ case 0: /* success */
+ break;
+
+ case NT_STATUS_BUFFER_OVERFLOW:
+ /* Not really an error, per se. Advisory. */
+ sr->smb2_status = status;
+ break;
+
+ case NT_STATUS_BUFFER_TOO_SMALL:
+ case NT_STATUS_INFO_LENGTH_MISMATCH:
+ /*
+ * These are special, per. [MS-SMB2] 3.2.5.17
+ * The error data is a 4-byte count of the size
+ * required to successfully query the data.
+ * That error data is built by the functions
+ * that returns one of these errors.
+ */
+ smb2sr_put_error_data(sr, status, &sr->raw_data);
+ goto out;
+
+ default:
+ smb2sr_put_error(sr, status);
+ goto out;
+ }
+
+ /*
+ * SMB2 Query Info reply
+ */
+ DataOff = SMB2_HDR_SIZE + 8;
+ oBufLength = MBC_LENGTH(&sr->raw_data);
+ rc = smb_mbc_encodef(
+ &sr->reply, "wwlC",
+ 9, /* StructSize */ /* w */
+ DataOff, /* w */
+ oBufLength, /* l */
+ &sr->raw_data); /* C */
+ if (rc)
+ sdrc = SDRC_ERROR;
+
+out:
+ kmem_free(qi, sizeof (*qi));
+
+ return (sdrc);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_read.c b/usr/src/uts/common/fs/smbsrv/smb2_read.c
new file mode 100644
index 0000000000..e269e71a1a
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_read.c
@@ -0,0 +1,153 @@
+/*
+ * 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 2015 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_READ
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+
+smb_sdrc_t
+smb2_read(smb_request_t *sr)
+{
+ smb_ofile_t *of = NULL;
+ smb_vdb_t *vdb = NULL;
+ struct mbuf *m = NULL;
+ uint16_t StructSize;
+ uint8_t Padding;
+ uint8_t DataOff;
+ uint32_t Length;
+ uint64_t Offset;
+ smb2fid_t smb2fid;
+ uint32_t MinCount;
+ uint32_t Channel;
+ uint32_t Remaining;
+ uint16_t ChanInfoOffset;
+ uint16_t ChanInfoLength;
+ uint32_t XferCount;
+ uint32_t status;
+ int rc = 0;
+
+ /*
+ * SMB2 Read request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data,
+ "wb.lqqqlllww",
+ &StructSize, /* w */
+ &Padding, /* b. */
+ &Length, /* l */
+ &Offset, /* q */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal, /* q */
+ &MinCount, /* l */
+ &Channel, /* l */
+ &Remaining, /* l */
+ &ChanInfoOffset, /* w */
+ &ChanInfoLength); /* w */
+ if (rc)
+ return (SDRC_ERROR);
+ if (StructSize != 49)
+ return (SDRC_ERROR);
+
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status) {
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+ }
+ of = sr->fid_ofile;
+
+ if (Length > smb2_max_rwsize) {
+ smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
+ return (SDRC_SUCCESS);
+ }
+ if (MinCount > Length)
+ MinCount = Length;
+
+ /* This is automatically free'd. */
+ vdb = smb_srm_zalloc(sr, sizeof (*vdb));
+ vdb->vdb_tag = 0;
+ vdb->vdb_uio.uio_iov = &vdb->vdb_iovec[0];
+ vdb->vdb_uio.uio_iovcnt = MAX_IOVEC;
+ vdb->vdb_uio.uio_resid = Length;
+ vdb->vdb_uio.uio_loffset = (offset_t)Offset;
+ vdb->vdb_uio.uio_segflg = UIO_SYSSPACE;
+ vdb->vdb_uio.uio_extflg = UIO_COPY_DEFAULT;
+
+ sr->raw_data.max_bytes = Length;
+ m = smb_mbuf_allocate(&vdb->vdb_uio);
+
+ switch (of->f_tree->t_res_type & STYPE_MASK) {
+ case STYPE_DISKTREE:
+ if (!smb_node_is_dir(of->f_node)) {
+ /* Check for conflicting locks. */
+ rc = smb_lock_range_access(sr, of->f_node,
+ Offset, Length, B_FALSE);
+ if (rc) {
+ rc = ERANGE;
+ break;
+ }
+ }
+ rc = smb_fsop_read(sr, of->f_cr, of->f_node, &vdb->vdb_uio);
+ break;
+ case STYPE_IPC:
+ rc = smb_opipe_read(sr, &vdb->vdb_uio);
+ break;
+ default:
+ case STYPE_PRINTQ:
+ rc = EACCES;
+ break;
+ }
+
+ /* How much data we moved. */
+ XferCount = Length - vdb->vdb_uio.uio_resid;
+
+ sr->raw_data.max_bytes = XferCount;
+ smb_mbuf_trim(m, XferCount);
+ MBC_ATTACH_MBUF(&sr->raw_data, m);
+
+ /*
+ * Checking the error return _after_ dealing with
+ * the returned data so that if m was allocated,
+ * it will be free'd via sr->raw_data cleanup.
+ */
+ if (rc) {
+ smb2sr_put_errno(sr, rc);
+ return (SDRC_SUCCESS);
+ }
+
+ /*
+ * SMB2 Read reply
+ */
+ DataOff = SMB2_HDR_SIZE + 16;
+ rc = smb_mbc_encodef(
+ &sr->reply,
+ "wb.lllC",
+ 17, /* StructSize */ /* w */
+ DataOff, /* b. */
+ XferCount, /* l */
+ 0, /* DataRemaining */ /* l */
+ 0, /* reserved */ /* l */
+ &sr->raw_data); /* C */
+ if (rc)
+ return (SDRC_ERROR);
+
+ mutex_enter(&of->f_mutex);
+ of->f_seek_pos = Offset + XferCount;
+ mutex_exit(&of->f_mutex);
+
+ return (SDRC_SUCCESS);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_session_setup.c b/usr/src/uts/common/fs/smbsrv/smb2_session_setup.c
new file mode 100644
index 0000000000..b7d66cf6a3
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_session_setup.c
@@ -0,0 +1,175 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_SESSION_SETUP
+ *
+ * Note that the Capabilities supplied in this request are an inferior
+ * subset of those given to us previously in the SMB2 Negotiate request.
+ * We need to remember the full set of capabilities from SMB2 Negotiate,
+ * and therefore ignore the subset of capabilities supplied here.
+ */
+
+#include <smbsrv/smb2_kproto.h>
+
+static void smb2_ss_adjust_credits(smb_request_t *);
+
+smb_sdrc_t
+smb2_session_setup(smb_request_t *sr)
+{
+ smb_arg_sessionsetup_t *sinfo;
+ uint16_t StructureSize;
+ uint8_t Flags;
+ uint8_t SecurityMode;
+ uint32_t Capabilities; /* ignored - see above */
+ uint32_t Channel;
+ uint16_t SecBufOffset;
+ uint16_t SecBufLength;
+ uint64_t PrevSessionId;
+ uint16_t SessionFlags;
+ uint32_t status;
+ int skip;
+ int rc = 0;
+
+ sinfo = smb_srm_zalloc(sr, sizeof (smb_arg_sessionsetup_t));
+ sr->sr_ssetup = sinfo;
+
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "wbbllwwq",
+ &StructureSize, /* w */
+ &Flags, /* b */
+ &SecurityMode, /* b */
+ &Capabilities, /* l */
+ &Channel, /* l */
+ &SecBufOffset, /* w */
+ &SecBufLength, /* w */
+ &PrevSessionId); /* q */
+ if (rc)
+ return (SDRC_ERROR);
+
+ /*
+ * We're normally positioned at the security buffer now,
+ * but there could be some padding before it.
+ */
+ skip = (SecBufOffset + sr->smb2_cmd_hdr) -
+ sr->smb_data.chain_offset;
+ if (skip < 0)
+ return (SDRC_ERROR);
+ if (skip > 0)
+ (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
+
+ /*
+ * Get the security buffer
+ */
+ sinfo->ssi_iseclen = SecBufLength;
+ sinfo->ssi_isecblob = smb_srm_zalloc(sr, sinfo->ssi_iseclen);
+ rc = smb_mbc_decodef(&sr->smb_data, "#c",
+ sinfo->ssi_iseclen, sinfo->ssi_isecblob);
+ if (rc)
+ return (SDRC_ERROR);
+
+ /*
+ * The real auth. work happens in here.
+ */
+ status = smb_authenticate_ext(sr);
+
+ SecBufOffset = SMB2_HDR_SIZE + 8;
+ SecBufLength = sinfo->ssi_oseclen;
+ SessionFlags = 0;
+
+ switch (status) {
+
+ case NT_STATUS_SUCCESS: /* Authenticated */
+ if (sr->uid_user->u_flags & SMB_USER_FLAG_GUEST)
+ SessionFlags |= SMB2_SESSION_FLAG_IS_GUEST;
+ if (sr->uid_user->u_flags & SMB_USER_FLAG_ANON)
+ SessionFlags |= SMB2_SESSION_FLAG_IS_NULL;
+ smb2_ss_adjust_credits(sr);
+ break;
+
+ /*
+ * This is not really an error, but tells the client
+ * it should send another session setup request.
+ * Not smb2_put_error because we send a payload.
+ */
+ case NT_STATUS_MORE_PROCESSING_REQUIRED:
+ sr->smb2_status = status;
+ break;
+
+ default:
+ SecBufLength = 0;
+ sr->smb2_status = status;
+ break;
+ }
+
+ /*
+ * SMB2 Session Setup reply
+ */
+
+ rc = smb_mbc_encodef(
+ &sr->reply,
+ "wwww#c",
+ 9, /* StructSize */ /* w */
+ SessionFlags, /* w */
+ SecBufOffset, /* w */
+ SecBufLength, /* w */
+ SecBufLength, /* # */
+ sinfo->ssi_osecblob); /* c */
+ if (rc)
+ return (SDRC_ERROR);
+
+ return (SDRC_SUCCESS);
+}
+
+/*
+ * After a successful authentication, raise s_max_credits up to the
+ * normal maximum that clients are allowed to request. Also, if we
+ * haven't yet given them their initial credits, do that now.
+ *
+ * Normally, clients will request some credits with session setup,
+ * but in case they don't request enough to raise s_cur_credits
+ * up to the configured initial_credits, increase the requested
+ * credits of this SR sufficiently to make that happen. The actual
+ * increase happens in the dispatch code after we return.
+ */
+static void
+smb2_ss_adjust_credits(smb_request_t *sr)
+{
+ smb_session_t *s = sr->session;
+
+ mutex_enter(&s->s_credits_mutex);
+ s->s_max_credits = s->s_cfg.skc_maximum_credits;
+
+ if (s->s_cur_credits < s->s_cfg.skc_initial_credits) {
+ uint16_t grant;
+
+ /* How many credits we want to grant with this SR. */
+ grant = s->s_cfg.skc_initial_credits - s->s_cur_credits;
+
+ /*
+ * Do we need to increase the smb2_credit_request?
+ * One might prefer to read this expression as:
+ * ((credit_request - credit_charge) < grant)
+ * but we know credit_charge == 1 and would rather not
+ * deal with a possibly negative value on the left,
+ * so adding credit_charge to both sides...
+ */
+ if (sr->smb2_credit_request < (grant + 1)) {
+ sr->smb2_credit_request = (grant + 1);
+ }
+ }
+
+ mutex_exit(&s->s_credits_mutex);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_set_info.c b/usr/src/uts/common/fs/smbsrv/smb2_set_info.c
new file mode 100644
index 0000000000..ba7c5eeaba
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_set_info.c
@@ -0,0 +1,120 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_SET_INFO
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <smbsrv/ntifs.h>
+
+smb_sdrc_t
+smb2_set_info(smb_request_t *sr)
+{
+ smb_setinfo_t sinfo;
+ uint16_t StructSize;
+ uint16_t iBufOffset;
+ uint32_t iBufLength;
+ uint32_t AddlInfo;
+ smb2fid_t smb2fid;
+ uint32_t status;
+ uint8_t InfoType, InfoClass;
+ smb_sdrc_t sdrc = SDRC_SUCCESS;
+ int rc = 0;
+
+ bzero(&sinfo, sizeof (sinfo));
+
+ /*
+ * SMB2 Set Info request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "wbblw..lqq",
+ &StructSize, /* w */
+ &InfoType, /* b */
+ &InfoClass, /* b */
+ &iBufLength, /* l */
+ &iBufOffset, /* w */
+ /* reserved .. */
+ &AddlInfo, /* l */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal); /* q */
+ if (rc || StructSize != 33) {
+ sdrc = SDRC_ERROR;
+ return (sdrc);
+ }
+
+ if (iBufLength > smb2_max_trans) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status)
+ goto errout;
+
+ sinfo.si_node = sr->fid_ofile->f_node;
+ sr->user_cr = sr->fid_ofile->f_cr;
+
+ /*
+ * If there's an input buffer, setup a shadow.
+ */
+ if (iBufLength) {
+ rc = MBC_SHADOW_CHAIN(&sinfo.si_data, &sr->smb_data,
+ sr->smb2_cmd_hdr + iBufOffset, iBufLength);
+ if (rc) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto errout;
+ }
+ }
+
+ /* No output data. */
+ sr->raw_data.max_bytes = 0;
+
+ switch (InfoType) {
+ case SMB2_0_INFO_FILE:
+ status = smb2_setinfo_file(sr, &sinfo, InfoClass);
+ break;
+ case SMB2_0_INFO_FILESYSTEM:
+ status = smb2_setinfo_fs(sr, &sinfo, InfoClass);
+ break;
+ case SMB2_0_INFO_SECURITY:
+ status = smb2_setinfo_sec(sr, &sinfo, AddlInfo);
+ break;
+ case SMB2_0_INFO_QUOTA:
+ status = smb2_setinfo_quota(sr, &sinfo);
+ break;
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (status) {
+ errout:
+ smb2sr_put_error(sr, status);
+ return (sdrc);
+ }
+
+ /*
+ * SMB2 Query Info reply
+ */
+ rc = smb_mbc_encodef(
+ &sr->reply, "w..",
+ 2); /* StructSize */ /* w */
+ if (rc)
+ sdrc = SDRC_ERROR;
+
+ return (sdrc);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_setinfo_file.c b/usr/src/uts/common/fs/smbsrv/smb2_setinfo_file.c
new file mode 100644
index 0000000000..37ec81366f
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_setinfo_file.c
@@ -0,0 +1,290 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_SET_INFO
+ *
+ * [MS-FSCC 2.4] If a file system does not support ...
+ * an Information Classs, NT_STATUS_INVALID_PARAMETER...
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <smbsrv/ntifs.h>
+
+static uint32_t smb2_setf_rename(smb_request_t *, smb_setinfo_t *);
+static uint32_t smb2_setf_link(smb_request_t *, smb_setinfo_t *);
+
+static uint32_t smb2_setf_seek(smb_request_t *, smb_setinfo_t *);
+static uint32_t smb2_setf_full_ea(smb_request_t *, smb_setinfo_t *);
+static uint32_t smb2_setf_mode(smb_request_t *, smb_setinfo_t *);
+
+static uint32_t smb2_setf_pipe(smb_request_t *, smb_setinfo_t *);
+static uint32_t smb2_setf_valid_len(smb_request_t *, smb_setinfo_t *);
+static uint32_t smb2_setf_shortname(smb_request_t *, smb_setinfo_t *);
+
+
+uint32_t
+smb2_setinfo_file(smb_request_t *sr, smb_setinfo_t *si, int InfoClass)
+{
+ smb_ofile_t *of = sr->fid_ofile;
+ uint32_t status;
+
+ si->si_node = of->f_node;
+
+ switch (InfoClass) {
+ case FileBasicInformation: /* 4 */
+ status = smb_set_basic_info(sr, si);
+ break;
+ case FileRenameInformation: /* 10 */
+ status = smb2_setf_rename(sr, si);
+ break;
+ case FileLinkInformation: /* 11 */
+ status = smb2_setf_link(sr, si);
+ break;
+ case FileDispositionInformation: /* 13 */
+ status = smb_set_disposition_info(sr, si);
+ break;
+ case FilePositionInformation: /* 14 */
+ status = smb2_setf_seek(sr, si);
+ break;
+ case FileFullEaInformation: /* 15 */
+ status = smb2_setf_full_ea(sr, si);
+ break;
+ case FileModeInformation: /* 16 */
+ status = smb2_setf_mode(sr, si);
+ break;
+ case FileAllocationInformation: /* 19 */
+ status = smb_set_alloc_info(sr, si);
+ break;
+ case FileEndOfFileInformation: /* 20 */
+ status = smb_set_eof_info(sr, si);
+ break;
+ case FilePipeInformation: /* 23 */
+ status = smb2_setf_pipe(sr, si);
+ break;
+ case FileValidDataLengthInformation: /* 39 */
+ status = smb2_setf_valid_len(sr, si);
+ break;
+ case FileShortNameInformation: /* 40 */
+ status = smb2_setf_shortname(sr, si);
+ break;
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ return (status);
+}
+
+
+/*
+ * FileRenameInformation
+ * See also: smb_set_rename_info()
+ */
+static uint32_t
+smb2_setf_rename(smb_request_t *sr, smb_setinfo_t *si)
+{
+ char *fname;
+ uint8_t flags;
+ uint64_t rootdir;
+ uint32_t namelen;
+ uint32_t status = 0;
+ int rc;
+
+ rc = smb_mbc_decodef(&si->si_data, "b7.ql",
+ &flags, &rootdir, &namelen);
+ if (rc == 0) {
+ rc = smb_mbc_decodef(&si->si_data, "%#U",
+ sr, namelen, &fname);
+ }
+ if (rc != 0)
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
+
+ if ((rootdir != 0) || (namelen == 0) || (namelen >= SMB_MAXPATHLEN)) {
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ status = smb_setinfo_rename(sr, si->si_node, fname, flags);
+
+ return (status);
+}
+
+/*
+ * FileLinkInformation
+ */
+static uint32_t
+smb2_setf_link(smb_request_t *sr, smb_setinfo_t *si)
+{
+ char *fname;
+ uint8_t flags;
+ uint64_t rootdir;
+ uint32_t namelen;
+ uint32_t status = 0;
+ int rc;
+
+ rc = smb_mbc_decodef(&si->si_data, "b7.ql",
+ &flags, &rootdir, &namelen);
+ if (rc == 0) {
+ rc = smb_mbc_decodef(&si->si_data, "%#U",
+ sr, namelen, &fname);
+ }
+ if (rc != 0)
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
+
+ if ((rootdir != 0) || (namelen == 0) || (namelen >= SMB_MAXPATHLEN)) {
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ status = smb_setinfo_link(sr, si->si_node, fname, flags);
+
+ return (status);
+}
+
+
+/*
+ * FilePositionInformation
+ */
+static uint32_t
+smb2_setf_seek(smb_request_t *sr, smb_setinfo_t *si)
+{
+ smb_ofile_t *of = sr->fid_ofile;
+ uint64_t newoff;
+
+ if (smb_mbc_decodef(&si->si_data, "q", &newoff) != 0)
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ ASSERT(of->f_magic == SMB_OFILE_MAGIC);
+ mutex_enter(&of->f_mutex);
+ of->f_seek_pos = newoff;
+ mutex_exit(&of->f_mutex);
+
+ return (0);
+}
+
+/*
+ * FileFullEaInformation
+ * We could put EAs in a named stream...
+ */
+/* ARGSUSED */
+static uint32_t
+smb2_setf_full_ea(smb_request_t *sr, smb_setinfo_t *si)
+{
+ return (NT_STATUS_EAS_NOT_SUPPORTED);
+}
+
+/*
+ * FileModeInformation [MS-FSCC 2.4.24]
+ * FILE_WRITE_THROUGH
+ * FILE_SEQUENTIAL_ONLY
+ * FILE_NO_INTERMEDIATE_BUFFERING
+ * etc.
+ */
+static uint32_t
+smb2_setf_mode(smb_request_t *sr, smb_setinfo_t *si)
+{
+ _NOTE(ARGUNUSED(sr))
+ uint32_t Mode;
+
+ if (smb_mbc_decodef(&si->si_data, "l", &Mode) != 0)
+ return (NT_STATUS_INVALID_PARAMETER);
+
+#if 0 /* XXX - todo */
+ if (Mode & FILE_WRITE_THROUGH) {
+ /* store this in the ofile */
+ }
+#endif
+
+ return (NT_STATUS_SUCCESS);
+}
+
+
+
+/*
+ * FilePipeInformation
+ */
+static uint32_t
+smb2_setf_pipe(smb_request_t *sr, smb_setinfo_t *si)
+{
+ _NOTE(ARGUNUSED(si))
+ smb_ofile_t *of = sr->fid_ofile;
+ uint32_t ReadMode;
+ uint32_t CompletionMode;
+ uint32_t status;
+
+ if (smb_mbc_decodef(&si->si_data, "ll",
+ &ReadMode, &CompletionMode) != 0)
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
+
+ switch (of->f_ftype) {
+ case SMB_FTYPE_BYTE_PIPE:
+ case SMB_FTYPE_MESG_PIPE:
+ /*
+ * XXX: Do we need to actually do anything with
+ * ReadMode or CompletionMode? If so, (later)
+ * store these in the opipe object.
+ *
+ * See also: smb2_sif_pipe()
+ */
+ status = 0;
+ break;
+ case SMB_FTYPE_DISK:
+ case SMB_FTYPE_PRINTER:
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return (status);
+}
+
+/*
+ * FileValidDataLengthInformation
+ */
+/* ARGSUSED */
+static uint32_t
+smb2_setf_valid_len(smb_request_t *sr, smb_setinfo_t *si)
+{
+ smb_ofile_t *of = sr->fid_ofile;
+ uint64_t eod;
+ int rc;
+
+ if (smb_mbc_decodef(&si->si_data, "q", &eod) != 0)
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
+
+ rc = smb_fsop_set_data_length(sr, of->f_cr, of->f_node, eod);
+ if (rc != 0)
+ return (smb_errno2status(rc));
+
+ return (0);
+}
+
+/*
+ * FileShortNameInformation
+ * We can (optionally) support supply short names,
+ * but you can't change them.
+ */
+static uint32_t
+smb2_setf_shortname(smb_request_t *sr, smb_setinfo_t *si)
+{
+ _NOTE(ARGUNUSED(si))
+ smb_ofile_t *of = sr->fid_ofile;
+
+ if (of->f_ftype != SMB_FTYPE_DISK)
+ return (NT_STATUS_INVALID_PARAMETER);
+ if ((of->f_tree->t_flags & SMB_TREE_SHORTNAMES) == 0)
+ return (NT_STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME);
+
+ return (NT_STATUS_ACCESS_DENIED);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_setinfo_fs.c b/usr/src/uts/common/fs/smbsrv/smb2_setinfo_fs.c
new file mode 100644
index 0000000000..eeee110672
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_setinfo_fs.c
@@ -0,0 +1,91 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_SET_INFO
+ *
+ * [MS-FSCC 2.5] If a file system does not implement ...
+ * an Information Classs, NT_STATUS_INVALID_PARAMETER...
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <smbsrv/ntifs.h>
+
+uint32_t smb2_setfs_control(smb_request_t *, smb_setinfo_t *);
+uint32_t smb2_setfs_obj_id(smb_request_t *, smb_setinfo_t *);
+
+uint32_t
+smb2_setinfo_fs(smb_request_t *sr, smb_setinfo_t *si, int InfoClass)
+{
+ uint32_t status;
+
+ switch (InfoClass) {
+
+ /* pg 153 */
+
+ case FileFsControlInformation: /* 6 */
+ status = smb2_setfs_control(sr, si);
+ break;
+ case FileFsObjectIdInformation: /* 8 */
+ status = smb2_setfs_obj_id(sr, si);
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ return (status);
+}
+
+/*
+ * FileFsControlInformation
+ */
+uint32_t
+smb2_setfs_control(smb_request_t *sr, smb_setinfo_t *si)
+{
+ _NOTE(ARGUNUSED(si))
+ smb_tree_t *tree = sr->tid_tree;
+
+ if (!STYPE_ISDSK(tree->t_res_type))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ return (0);
+}
+
+/*
+ * FileFsObjectIdInformation
+ */
+/* ARGSUSED */
+uint32_t
+smb2_setfs_obj_id(smb_request_t *sr, smb_setinfo_t *si)
+{
+ /*
+ * Return an error per. [MS-FSCC 2.5.7]
+ * which means we can't change object IDs.
+ */
+ return (NT_STATUS_INVALID_PARAMETER);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_setinfo_quota.c b/usr/src/uts/common/fs/smbsrv/smb2_setinfo_quota.c
new file mode 100644
index 0000000000..bdeda05ba7
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_setinfo_quota.c
@@ -0,0 +1,89 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_SET_INFO
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <smbsrv/ntifs.h>
+
+/*
+ * Similar to smb_nt_transact_set_quota()
+ */
+uint32_t
+smb2_setinfo_quota(smb_request_t *sr, smb_setinfo_t *si)
+{
+ char *root_path;
+ uint32_t status = NT_STATUS_SUCCESS;
+ smb_ofile_t *ofile = sr->fid_ofile;
+ smb_node_t *tnode;
+ smb_quota_set_t request;
+ uint32_t reply;
+ list_t *quota_list;
+
+ bzero(&request, sizeof (smb_quota_set_t));
+
+ if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA))
+ return (NT_STATUS_NOT_SUPPORTED);
+
+ if (!smb_user_is_admin(sr->uid_user))
+ return (NT_STATUS_ACCESS_DENIED);
+
+ if ((ofile->f_node == NULL) ||
+ (ofile->f_ftype != SMB_FTYPE_DISK))
+ return (NT_STATUS_ACCESS_DENIED);
+
+ tnode = sr->tid_tree->t_snode;
+ root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+ if (smb_node_getmntpath(tnode, root_path, MAXPATHLEN) != 0) {
+ smbsr_release_file(sr);
+ kmem_free(root_path, MAXPATHLEN);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ quota_list = &request.qs_quota_list;
+ list_create(quota_list, sizeof (smb_quota_t),
+ offsetof(smb_quota_t, q_list_node));
+
+ status = smb_quota_decode_quotas(&si->si_data, quota_list);
+ if (status == NT_STATUS_SUCCESS) {
+ request.qs_root_path = root_path;
+ if (smb_quota_set(sr->sr_server, &request, &reply) != 0) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ } else {
+ status = reply;
+ xdr_free(xdr_uint32_t, (char *)&reply);
+ }
+ }
+
+ kmem_free(root_path, MAXPATHLEN);
+ smb_quota_free_quotas(&request.qs_quota_list);
+ smbsr_release_file(sr);
+
+ return (status);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_setinfo_sec.c b/usr/src/uts/common/fs/smbsrv/smb2_setinfo_sec.c
new file mode 100644
index 0000000000..8c621d58ce
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_setinfo_sec.c
@@ -0,0 +1,80 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_SET_INFO
+ * Similar to smb_nt_transact_security.c
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <smbsrv/ntifs.h>
+
+uint32_t
+smb2_setinfo_sec(smb_request_t *sr, smb_setinfo_t *si, uint32_t secinfo)
+{
+ smb_sd_t sd;
+ uint32_t status;
+
+ /*
+ * secinfo & ...
+ * OWNER_SECURITY_INFORMATION,
+ * GROUP_SECURITY_INFORMATION,
+ * DACL_SECURITY_INFORMATION, ...
+ */
+
+ if ((sr->fid_ofile->f_node == NULL) ||
+ (sr->fid_ofile->f_ftype != SMB_FTYPE_DISK))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ if (SMB_TREE_IS_READONLY(sr))
+ return (NT_STATUS_MEDIA_WRITE_PROTECTED);
+
+ if (sr->tid_tree->t_acltype != ACE_T) {
+ /*
+ * If target filesystem doesn't support ACE_T acls then
+ * don't process SACL
+ */
+ secinfo &= ~SMB_SACL_SECINFO;
+ }
+
+ if ((secinfo & SMB_ALL_SECINFO) == 0)
+ return (NT_STATUS_SUCCESS);
+
+ status = smb_decode_sd(&si->si_data, &sd);
+ if (status != NT_STATUS_SUCCESS)
+ return (status);
+
+ if (((secinfo & SMB_OWNER_SECINFO) && (sd.sd_owner == NULL)) ||
+ ((secinfo & SMB_GROUP_SECINFO) && (sd.sd_group == NULL)))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ if (!smb_node_is_system(sr->fid_ofile->f_node))
+ status = smb_sd_write(sr, &sd, secinfo);
+
+ smb_sd_term(&sd);
+ return (status);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_signing.c b/usr/src/uts/common/fs/smbsrv/smb2_signing.c
new file mode 100644
index 0000000000..25caed6ad2
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_signing.c
@@ -0,0 +1,325 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ */
+/*
+ * These routines provide the SMB MAC signing for the SMB2 server.
+ * The routines calculate the signature of a SMB message in an mbuf chain.
+ *
+ * The following table describes the client server
+ * signing registry relationship
+ *
+ * | Required | Enabled | Disabled
+ * -------------+---------------+------------ +--------------
+ * Required | Signed | Signed | Fail
+ * -------------+---------------+-------------+-----------------
+ * Enabled | Signed | Signed | Not Signed
+ * -------------+---------------+-------------+----------------
+ * Disabled | Fail | Not Signed | Not Signed
+ */
+
+#include <sys/uio.h>
+#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb_signing.h>
+#include <sys/isa_defs.h>
+#include <sys/byteorder.h>
+#include <sys/cmn_err.h>
+
+#define SMB2_SIG_OFFS 48
+#define SMB2_SIG_SIZE 16
+
+/*
+ * Called during session destroy.
+ */
+static void
+smb2_sign_fini(smb_session_t *s)
+{
+ smb_sign_mech_t *mech;
+
+ if ((mech = s->sign_mech) != NULL) {
+ kmem_free(mech, sizeof (*mech));
+ s->sign_mech = NULL;
+ }
+}
+
+/*
+ * smb2_sign_begin
+ *
+ * Get the mechanism info.
+ * Intializes MAC key based on the user session key and store it in
+ * the signing structure. This begins signing on this session.
+ */
+int
+smb2_sign_begin(smb_request_t *sr, smb_token_t *token)
+{
+ smb_session_t *s = sr->session;
+ smb_user_t *u = sr->uid_user;
+ struct smb_key *sign_key = &u->u_sign_key;
+ smb_sign_mech_t *mech;
+ int rc;
+
+ /*
+ * We should normally have a session key here because
+ * our caller filters out Anonymous and Guest logons.
+ * However, buggy clients could get us here without a
+ * session key, in which case we'll fail later when a
+ * request that requires signing can't be checked.
+ */
+ if (token->tkn_ssnkey.val == NULL || token->tkn_ssnkey.len == 0)
+ return (0);
+
+ /*
+ * Session-level initialization (once per session)
+ * Get mech handle, sign_fini function.
+ */
+ smb_rwx_rwenter(&s->s_lock, RW_WRITER);
+ if (s->sign_mech == NULL) {
+ mech = kmem_zalloc(sizeof (*mech), KM_SLEEP);
+ rc = smb2_hmac_getmech(mech);
+ if (rc != 0) {
+ kmem_free(mech, sizeof (*mech));
+ smb_rwx_rwexit(&s->s_lock);
+ return (rc);
+ }
+ s->sign_mech = mech;
+ s->sign_fini = smb2_sign_fini;
+ }
+ smb_rwx_rwexit(&s->s_lock);
+
+ /*
+ * Compute and store the signing key, which lives in
+ * the user structure.
+ */
+ sign_key->len = SMB2_SIG_SIZE;
+
+ /*
+ * For SMB2, the signing key is just the first 16 bytes
+ * of the session key (truncated or padded with zeros).
+ * [MS-SMB2] 3.2.5.3.1
+ */
+ bcopy(token->tkn_ssnkey.val, sign_key->key,
+ MIN(token->tkn_ssnkey.len, sign_key->len));
+
+ mutex_enter(&u->u_mutex);
+ if (s->secmode & SMB2_NEGOTIATE_SIGNING_ENABLED)
+ u->u_sign_flags |= SMB_SIGNING_ENABLED;
+ if (s->secmode & SMB2_NEGOTIATE_SIGNING_REQUIRED)
+ u->u_sign_flags |=
+ SMB_SIGNING_ENABLED | SMB_SIGNING_CHECK;
+ mutex_exit(&u->u_mutex);
+
+ /*
+ * If we just turned on signing, the current request
+ * (an SMB2 session setup) will have come in without
+ * SMB2_FLAGS_SIGNED (and not signed) but the response
+ * is is supposed to be signed. [MS-SMB2] 3.3.5.5
+ */
+ if (u->u_sign_flags & SMB_SIGNING_ENABLED)
+ sr->smb2_hdr_flags |= SMB2_FLAGS_SIGNED;
+
+ return (0);
+}
+
+/*
+ * smb2_sign_calc
+ *
+ * Calculates MAC signature for the given buffer and returns
+ * it in the mac_sign parameter.
+ *
+ * The signature is in the last 16 bytes of the SMB2 header.
+ * The signature algorighm is to compute HMAC SHA256 over the
+ * entire command, with the signature field set to zeros.
+ *
+ * Return 0 if success else -1
+ */
+static int
+smb2_sign_calc(smb_request_t *sr, struct mbuf_chain *mbc,
+ uint8_t *digest)
+{
+ uint8_t tmp_hdr[SMB2_HDR_SIZE];
+ smb_sign_ctx_t ctx = 0;
+ smb_session_t *s = sr->session;
+ smb_user_t *u = sr->uid_user;
+ struct smb_key *sign_key = &u->u_sign_key;
+ struct mbuf *mbuf;
+ int offset, resid, tlen, rc;
+
+ if (s->sign_mech == NULL || sign_key->len == 0)
+ return (-1);
+
+ rc = smb2_hmac_init(&ctx, s->sign_mech, sign_key->key, sign_key->len);
+ if (rc != 0)
+ return (rc);
+
+ /*
+ * Work with a copy of the SMB2 header so we can
+ * clear the signature field without modifying
+ * the original message.
+ */
+ tlen = SMB2_HDR_SIZE;
+ offset = mbc->chain_offset;
+ resid = mbc->max_bytes - offset;
+ if (smb_mbc_peek(mbc, offset, "#c", tlen, tmp_hdr) != 0)
+ return (-1);
+ bzero(tmp_hdr + SMB2_SIG_OFFS, SMB2_SIG_SIZE);
+ if ((rc = smb2_hmac_update(ctx, tmp_hdr, tlen)) != 0)
+ return (rc);
+ offset += tlen;
+ resid -= tlen;
+
+ /*
+ * Digest the rest of the SMB packet, starting at the data
+ * just after the SMB header.
+ *
+ * Advance to the src mbuf where we start digesting.
+ */
+ mbuf = mbc->chain;
+ while (mbuf != NULL && (offset >= mbuf->m_len)) {
+ offset -= mbuf->m_len;
+ mbuf = mbuf->m_next;
+ }
+
+ if (mbuf == NULL)
+ return (-1);
+
+ /*
+ * Digest the remainder of this mbuf, limited to the
+ * residual count, and starting at the current offset.
+ * (typically SMB2_HDR_SIZE)
+ */
+ tlen = mbuf->m_len - offset;
+ if (tlen > resid)
+ tlen = resid;
+ rc = smb2_hmac_update(ctx, (uint8_t *)mbuf->m_data + offset, tlen);
+ if (rc != 0)
+ return (rc);
+ resid -= tlen;
+
+ /*
+ * Digest any more mbufs in the chain.
+ */
+ while (resid > 0) {
+ mbuf = mbuf->m_next;
+ if (mbuf == NULL)
+ return (-1);
+ tlen = mbuf->m_len;
+ if (tlen > resid)
+ tlen = resid;
+ rc = smb2_hmac_update(ctx, (uint8_t *)mbuf->m_data, tlen);
+ if (rc != 0)
+ return (rc);
+ resid -= tlen;
+ }
+
+ /*
+ * Note: digest is _always_ SMB2_SIG_SIZE,
+ * even if the mech uses a longer one.
+ */
+ if ((rc = smb2_hmac_final(ctx, digest)) != 0)
+ return (rc);
+
+ return (0);
+}
+
+/*
+ * smb2_sign_check_request
+ *
+ * Calculates MAC signature for the request mbuf chain
+ * using the next expected sequence number and compares
+ * it to the given signature.
+ *
+ * Note it does not check the signature for secondary transactions
+ * as their sequence number is the same as the original request.
+ *
+ * Return 0 if the signature verifies, otherwise, returns -1;
+ *
+ */
+int
+smb2_sign_check_request(smb_request_t *sr)
+{
+ uint8_t req_sig[SMB2_SIG_SIZE];
+ uint8_t vfy_sig[SMB2_SIG_SIZE];
+ struct mbuf_chain *mbc = &sr->smb_data;
+ smb_user_t *u = sr->uid_user;
+ int sig_off;
+
+ /*
+ * Don't check commands with a zero session ID.
+ * [MS-SMB2] 3.3.4.1.1
+ */
+ if (sr->smb_uid == 0 || u == NULL)
+ return (0);
+
+ /* Get the request signature. */
+ sig_off = sr->smb2_cmd_hdr + SMB2_SIG_OFFS;
+ if (smb_mbc_peek(mbc, sig_off, "#c", SMB2_SIG_SIZE, req_sig) != 0)
+ return (-1);
+
+ /*
+ * Compute the correct signature and compare.
+ */
+ if (smb2_sign_calc(sr, mbc, vfy_sig) != 0)
+ return (-1);
+ if (memcmp(vfy_sig, req_sig, SMB2_SIG_SIZE) != 0) {
+ cmn_err(CE_NOTE, "smb2_sign_check_request: bad signature");
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * smb2_sign_reply
+ *
+ * Calculates MAC signature for the given mbuf chain,
+ * and write it to the signature field in the mbuf.
+ *
+ */
+void
+smb2_sign_reply(smb_request_t *sr)
+{
+ uint8_t reply_sig[SMB2_SIG_SIZE];
+ struct mbuf_chain tmp_mbc;
+ smb_user_t *u = sr->uid_user;
+ int hdr_off, msg_len;
+
+ if (u == NULL)
+ return;
+
+ msg_len = sr->reply.chain_offset - sr->smb2_reply_hdr;
+ (void) MBC_SHADOW_CHAIN(&tmp_mbc, &sr->reply,
+ sr->smb2_reply_hdr, msg_len);
+
+ /*
+ * Calculate the MAC signature for this reply.
+ */
+ if (smb2_sign_calc(sr, &tmp_mbc, reply_sig) != 0)
+ return;
+
+ /*
+ * Poke the signature into the response.
+ */
+ hdr_off = sr->smb2_reply_hdr + SMB2_SIG_OFFS;
+ (void) smb_mbc_poke(&sr->reply, hdr_off, "#c",
+ SMB2_SIG_SIZE, reply_sig);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c b/usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c
new file mode 100644
index 0000000000..840742e40a
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c
@@ -0,0 +1,113 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_TREE_CONNECT
+ */
+
+#include <smbsrv/smb2_kproto.h>
+
+smb_sdrc_t
+smb2_tree_connect(smb_request_t *sr)
+{
+ smb_arg_tcon_t *tcon = &sr->sr_tcon;
+ smb_tree_t *tree = NULL;
+ uint16_t StructureSize;
+ uint16_t PathOffset;
+ uint16_t PathLength;
+ uint8_t ShareType;
+ uint32_t ShareFlags;
+ uint32_t Capabilities;
+ uint32_t status;
+ int skip;
+ int rc = 0;
+
+ /*
+ * SMB2 Tree Connect request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "w..ww",
+ &StructureSize,
+ /* reserved */
+ &PathOffset,
+ &PathLength);
+ if (rc)
+ return (SDRC_ERROR);
+
+ /*
+ * We're normally positioned at the path name now,
+ * but there could be some padding before it.
+ */
+ skip = (PathOffset + sr->smb2_cmd_hdr) -
+ sr->smb_data.chain_offset;
+ if (skip < 0)
+ return (SDRC_ERROR);
+ if (skip > 0)
+ (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
+
+ /*
+ * Get the path name
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "%#U",
+ sr, (uint_t)PathLength, &tcon->path);
+ if (rc)
+ return (SDRC_ERROR);
+
+ status = smb_tree_connect(sr);
+ if (status) {
+ (void) smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+ }
+ tree = sr->tid_tree;
+
+ /*
+ * Report the share type.
+ */
+ switch (tree->t_res_type & STYPE_MASK) {
+ case STYPE_IPC:
+ ShareType = SMB2_SHARE_TYPE_PIPE;
+ break;
+ case STYPE_PRINTQ:
+ ShareType = SMB2_SHARE_TYPE_PRINT;
+ break;
+ case STYPE_DISKTREE:
+ default:
+ ShareType = SMB2_SHARE_TYPE_DISK;
+ break;
+ }
+
+ /*
+ * XXX These need work..
+ */
+ ShareFlags = 0;
+ Capabilities = 0;
+
+ /*
+ * SMB2 Tree Connect reply
+ */
+ rc = smb_mbc_encodef(
+ &sr->reply,
+ "wb.lll",
+ 16, /* StructSize */ /* w */
+ ShareType, /* b */
+ ShareFlags, /* l */
+ Capabilities, /* l */
+ tree->t_access); /* l */
+ if (rc)
+ return (SDRC_ERROR);
+
+ return (SDRC_SUCCESS);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_tree_disconn.c b/usr/src/uts/common/fs/smbsrv/smb2_tree_disconn.c
new file mode 100644
index 0000000000..4306b1363f
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_tree_disconn.c
@@ -0,0 +1,56 @@
+/*
+ * 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 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_TREE_DISCONNECT
+ */
+
+#include <smbsrv/smb2_kproto.h>
+
+smb_sdrc_t
+smb2_tree_disconn(smb_request_t *sr)
+{
+ uint16_t StructSize;
+ uint16_t reserved;
+ int rc;
+
+ /*
+ * SMB2 Tree Disconnect request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data, "ww",
+ &StructSize, /* w */
+ &reserved); /* w */
+ if (rc)
+ return (SDRC_ERROR);
+ if (StructSize != 4)
+ return (SDRC_ERROR);
+
+ if (sr->uid_user == NULL || sr->tid_tree == NULL)
+ return (SDRC_ERROR);
+
+ smb_session_cancel_requests(sr->session, sr->tid_tree, sr);
+ smb_tree_disconnect(sr->tid_tree, B_TRUE);
+
+ /*
+ * SMB2 Tree Disconnect reply
+ */
+ (void) smb_mbc_encodef(
+ &sr->reply, "wwl",
+ 4, /* StructSize */ /* w */
+ 0); /* reserved */ /* w */
+
+ return (SDRC_SUCCESS);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_write.c b/usr/src/uts/common/fs/smbsrv/smb2_write.c
new file mode 100644
index 0000000000..6b1bd3b837
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb2_write.c
@@ -0,0 +1,165 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Dispatch function for SMB2_WRITE
+ */
+
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_fsops.h>
+
+smb_sdrc_t
+smb2_write(smb_request_t *sr)
+{
+ smb_ofile_t *of = NULL;
+ smb_vdb_t *vdb = NULL;
+ uint16_t StructSize;
+ uint16_t DataOff;
+ uint32_t Length;
+ uint64_t Offset;
+ smb2fid_t smb2fid;
+ uint32_t Channel;
+ uint32_t Remaining;
+ uint16_t ChanInfoOffset;
+ uint16_t ChanInfoLength;
+ uint32_t Flags;
+ uint32_t XferCount;
+ uint32_t status;
+ int data_chain_off, skip;
+ int stability = 0;
+ int rc = 0;
+
+ /*
+ * SMB2 Write request
+ */
+ rc = smb_mbc_decodef(
+ &sr->smb_data,
+ "wwlqqqllwwl",
+ &StructSize, /* w */
+ &DataOff, /* w */
+ &Length, /* l */
+ &Offset, /* q */
+ &smb2fid.persistent, /* q */
+ &smb2fid.temporal, /* q */
+ &Channel, /* l */
+ &Remaining, /* l */
+ &ChanInfoOffset, /* w */
+ &ChanInfoLength, /* w */
+ &Flags); /* l */
+ if (rc)
+ return (SDRC_ERROR);
+ if (StructSize != 49)
+ return (SDRC_ERROR);
+
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ if (status) {
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+ }
+ of = sr->fid_ofile;
+
+ if (Length > smb2_max_rwsize) {
+ smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
+ return (SDRC_SUCCESS);
+ }
+
+ /*
+ * Skip any padding before the write data.
+ */
+ data_chain_off = sr->smb2_cmd_hdr + DataOff;
+ skip = data_chain_off - sr->smb_data.chain_offset;
+ if (skip < 0) {
+ smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
+ return (SDRC_SUCCESS);
+ }
+ if (skip > 0) {
+ (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
+ }
+
+ /* This is automatically free'd. */
+ vdb = smb_srm_zalloc(sr, sizeof (*vdb));
+ rc = smb_mbc_decodef(&sr->smb_data, "#B", Length, vdb);
+ if (rc != 0 || vdb->vdb_len != Length) {
+ smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
+ return (SDRC_SUCCESS);
+ }
+ vdb->vdb_uio.uio_loffset = (offset_t)Offset;
+
+ XferCount = 0;
+ if (Length == 0)
+ goto doreply;
+
+ switch (of->f_tree->t_res_type & STYPE_MASK) {
+ case STYPE_DISKTREE:
+ case STYPE_PRINTQ:
+ if (!smb_node_is_dir(of->f_node)) {
+ /* Check for conflicting locks. */
+ rc = smb_lock_range_access(sr, of->f_node,
+ Offset, Length, B_TRUE);
+ if (rc) {
+ rc = ERANGE;
+ break;
+ }
+ }
+ if ((Flags & SMB2_WRITEFLAG_WRITE_THROUGH) ||
+ (of->f_node->flags & NODE_FLAGS_WRITE_THROUGH)) {
+ stability = FSYNC;
+ }
+ rc = smb_fsop_write(sr, of->f_cr, of->f_node,
+ &vdb->vdb_uio, &XferCount, stability);
+ if (rc)
+ break;
+ of->f_written = B_TRUE;
+ if (!smb_node_is_dir(of->f_node))
+ smb_oplock_break_levelII(of->f_node);
+ break;
+
+ case STYPE_IPC:
+ rc = smb_opipe_write(sr, &vdb->vdb_uio);
+ if (rc == 0)
+ XferCount = Length;
+ break;
+
+ default:
+ rc = EACCES;
+ break;
+ }
+
+ if (rc) {
+ smb2sr_put_errno(sr, rc);
+ return (SDRC_SUCCESS);
+ }
+
+ /*
+ * SMB2 Write reply
+ */
+doreply:
+ DataOff = SMB2_HDR_SIZE + 16;
+ rc = smb_mbc_encodef(
+ &sr->reply, "wwlll",
+ 17, /* StructSize */ /* w */
+ 0, /* reserved */ /* w */
+ XferCount, /* l */
+ 0, /* DataRemaining */ /* l */
+ 0); /* Channel Info */ /* l */
+ if (rc)
+ return (SDRC_ERROR);
+
+ mutex_enter(&of->f_mutex);
+ of->f_seek_pos = Offset + XferCount;
+ mutex_exit(&of->f_mutex);
+
+ return (SDRC_SUCCESS);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_authenticate.c b/usr/src/uts/common/fs/smbsrv/smb_authenticate.c
index ffc553cada..a208e1552e 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_authenticate.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_authenticate.c
@@ -390,6 +390,7 @@ smb_auth_get_token(smb_request_t *sr)
uint32_t rlen = 0;
uint32_t privileges;
uint32_t status;
+ int rc;
bool_t ok;
msg_hdr.lmh_msgtype = LSA_MTYPE_GETTOK;
@@ -450,7 +451,12 @@ smb_auth_get_token(smb_request_t *sr)
* but only for real logon (not ANON or GUEST).
*/
if ((token->tkn_flags & (SMB_ATF_GUEST | SMB_ATF_ANON)) == 0) {
- if (smb_sign_begin(sr, token) != 0) {
+ if (sr->session->dialect >= SMB_VERS_2_BASE) {
+ rc = smb2_sign_begin(sr, token);
+ } else {
+ rc = smb_sign_begin(sr, token);
+ }
+ if (rc != 0) {
status = NT_STATUS_INTERNAL_ERROR;
goto errout;
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_cmn_rename.c b/usr/src/uts/common/fs/smbsrv/smb_cmn_rename.c
new file mode 100644
index 0000000000..0cc35b30ac
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb_cmn_rename.c
@@ -0,0 +1,692 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#include <sys/synch.h>
+#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <sys/nbmlock.h>
+
+/*
+ * SMB_TRANS2_SET_FILE/PATH_INFO (RENAME_INFORMATION level) flag
+ */
+#define SMB_RENAME_FLAG_OVERWRITE 0x001
+
+static int smb_rename_check_stream(smb_fqi_t *, smb_fqi_t *);
+static int smb_rename_check_attr(smb_request_t *, smb_node_t *, uint16_t);
+static int smb_rename_lookup_src(smb_request_t *);
+static void smb_rename_release_src(smb_request_t *);
+static uint32_t smb_rename_errno2status(int);
+
+/*
+ * smb_setinfo_rename
+ *
+ * Implements SMB_FILE_RENAME_INFORMATION level of Trans2_Set_FileInfo
+ * and Trans2_Set_PathInfo and SMB2 set_info, FileRenameInformation.
+ * If the new filename (dst_fqi) already exists it may be overwritten
+ * if flags == 1.
+ *
+ * The passed path is a full path relative to the share root.
+ *
+ * Returns NT status codes.
+ *
+ * Similar to smb_setinfo_link(), below.
+ */
+uint32_t
+smb_setinfo_rename(smb_request_t *sr, smb_node_t *node, char *path, int flags)
+{
+ smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
+ smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
+ smb_pathname_t *dst_pn = &dst_fqi->fq_path;
+ uint32_t status;
+
+ sr->arg.dirop.flags = flags ? SMB_RENAME_FLAG_OVERWRITE : 0;
+ sr->arg.dirop.info_level = FileRenameInformation;
+
+ src_fqi->fq_sattr = SMB_SEARCH_ATTRIBUTES;
+ src_fqi->fq_fnode = node;
+ src_fqi->fq_dnode = node->n_dnode;
+
+ /* validate the dst pathname */
+ smb_pathname_init(sr, dst_pn, path);
+ if (!smb_pathname_validate(sr, dst_pn))
+ return (NT_STATUS_OBJECT_NAME_INVALID);
+
+ status = smb_common_rename(sr, src_fqi, dst_fqi);
+ return (status);
+}
+
+/*
+ * smb_common_rename
+ *
+ * Common code for renaming a file.
+ *
+ * If the source and destination are identical, we go through all
+ * the checks but we don't actually do the rename. If the source
+ * and destination files differ only in case, we do a case-sensitive
+ * rename. Otherwise, we do a full case-insensitive rename.
+ *
+ * Returns NT status values.
+ *
+ * Similar to smb_make_link(), below.
+ */
+uint32_t
+smb_common_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
+{
+ smb_node_t *src_fnode, *src_dnode, *dst_dnode;
+ smb_node_t *dst_fnode = 0;
+ smb_node_t *tnode;
+ char *new_name, *path;
+ DWORD status;
+ int rc, count;
+
+ tnode = sr->tid_tree->t_snode;
+ path = dst_fqi->fq_path.pn_path;
+
+ /* Check if attempting to rename a stream - not yet supported */
+ rc = smb_rename_check_stream(src_fqi, dst_fqi);
+ if (rc != 0)
+ return (smb_rename_errno2status(rc));
+
+ /*
+ * The source node may already have been provided,
+ * i.e. when called by SMB1/SMB2 smb_setinfo_rename.
+ * Not provided by smb_com_rename, smb_com_nt_rename.
+ */
+ if (src_fqi->fq_fnode) {
+ smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
+ smb_node_ref(src_fqi->fq_fnode);
+ smb_node_ref(src_fqi->fq_dnode);
+ } else {
+ /* lookup and validate src node */
+ rc = smb_rename_lookup_src(sr);
+ if (rc != 0)
+ return (smb_rename_errno2status(rc));
+ }
+
+ src_fnode = src_fqi->fq_fnode;
+ src_dnode = src_fqi->fq_dnode;
+
+ /*
+ * Find the destination dnode and last component.
+ * May already be provided, i.e. when called via
+ * SMB1 trans2 setinfo.
+ */
+ if (dst_fqi->fq_dnode) {
+ /* called via smb_set_rename_info */
+ smb_node_ref(dst_fqi->fq_dnode);
+ } else {
+ /* called via smb2_setf_rename, smb_com_rename, etc. */
+ rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
+ &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
+ if (rc != 0) {
+ smb_rename_release_src(sr);
+ return (smb_rename_errno2status(rc));
+ }
+ }
+
+ dst_dnode = dst_fqi->fq_dnode;
+ new_name = dst_fqi->fq_last_comp;
+
+ /* If exact name match in same directory, we're done */
+ if ((src_dnode == dst_dnode) &&
+ (strcmp(src_fnode->od_name, new_name) == 0)) {
+ smb_rename_release_src(sr);
+ smb_node_release(dst_dnode);
+ return (0);
+ }
+
+ /* Lookup destination node */
+ rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
+ dst_dnode, new_name, &dst_fqi->fq_fnode);
+
+ /* If the destination node doesn't already exist, validate new_name. */
+ if (rc == ENOENT) {
+ if (smb_is_invalid_filename(new_name)) {
+ smb_rename_release_src(sr);
+ smb_node_release(dst_dnode);
+ return (NT_STATUS_OBJECT_NAME_INVALID);
+ }
+ }
+
+ /*
+ * Handle case where changing case of the same directory entry.
+ *
+ * If we found the dst node in the same directory as the src node,
+ * and their names differ only in case:
+ *
+ * If the tree is case sensitive (or mixed):
+ * Do case sensitive lookup to see if exact match exists.
+ * If the exact match is the same node as src_node we're done.
+ *
+ * If the tree is case insensitive:
+ * There is currently no way to tell if the case is different
+ * or not, so do the rename (unless the specified new name was
+ * mangled).
+ */
+ if ((rc == 0) &&
+ (src_dnode == dst_dnode) &&
+ (smb_strcasecmp(src_fnode->od_name,
+ dst_fqi->fq_fnode->od_name, 0) == 0)) {
+ smb_node_release(dst_fqi->fq_fnode);
+ dst_fqi->fq_fnode = NULL;
+
+ if (smb_tree_has_feature(sr->tid_tree,
+ SMB_TREE_NO_CASESENSITIVE)) {
+ if (smb_strcasecmp(src_fnode->od_name,
+ dst_fqi->fq_last_comp, 0) != 0) {
+ smb_rename_release_src(sr);
+ smb_node_release(dst_dnode);
+ return (0);
+ }
+ } else {
+ rc = smb_fsop_lookup(sr, sr->user_cr,
+ SMB_CASE_SENSITIVE, tnode, dst_dnode, new_name,
+ &dst_fqi->fq_fnode);
+
+ if ((rc == 0) &&
+ (dst_fqi->fq_fnode == src_fnode)) {
+ smb_rename_release_src(sr);
+ smb_node_release(dst_fqi->fq_fnode);
+ smb_node_release(dst_dnode);
+ return (0);
+ }
+ }
+ }
+
+ if ((rc != 0) && (rc != ENOENT)) {
+ smb_rename_release_src(sr);
+ smb_node_release(dst_fqi->fq_dnode);
+ return (smb_rename_errno2status(rc));
+ }
+
+ if (dst_fqi->fq_fnode) {
+ /*
+ * Destination already exists. Do delete checks.
+ */
+ dst_fnode = dst_fqi->fq_fnode;
+
+ if (!(sr->arg.dirop.flags && SMB_RENAME_FLAG_OVERWRITE)) {
+ smb_rename_release_src(sr);
+ smb_node_release(dst_fnode);
+ smb_node_release(dst_dnode);
+ return (NT_STATUS_OBJECT_NAME_COLLISION);
+ }
+
+ (void) smb_oplock_break(sr, dst_fnode,
+ SMB_OPLOCK_BREAK_TO_NONE | SMB_OPLOCK_BREAK_BATCH);
+
+ /*
+ * Wait (a little) for the oplock break to be
+ * responded to by clients closing handles.
+ * Hold node->n_lock as reader to keep new
+ * ofiles from showing up after we check.
+ */
+ smb_node_rdlock(dst_fnode);
+ for (count = 0; count <= 12; count++) {
+ status = smb_node_delete_check(dst_fnode);
+ if (status != NT_STATUS_SHARING_VIOLATION)
+ break;
+ smb_node_unlock(dst_fnode);
+ delay(MSEC_TO_TICK(100));
+ smb_node_rdlock(dst_fnode);
+ }
+ if (status != NT_STATUS_SUCCESS) {
+ smb_node_unlock(dst_fnode);
+ smb_rename_release_src(sr);
+ smb_node_release(dst_fnode);
+ smb_node_release(dst_dnode);
+ return (NT_STATUS_ACCESS_DENIED);
+ }
+
+ /*
+ * Note, the combination of these two:
+ * smb_node_rdlock(node);
+ * nbl_start_crit(node->vp, RW_READER);
+ * is equivalent to this call:
+ * smb_node_start_crit(node, RW_READER)
+ *
+ * Cleanup after this point should use:
+ * smb_node_end_crit(dst_fnode)
+ */
+ nbl_start_crit(dst_fnode->vp, RW_READER);
+
+ /*
+ * This checks nbl_share_conflict, nbl_lock_conflict
+ */
+ status = smb_nbl_conflict(dst_fnode, 0, UINT64_MAX, NBL_REMOVE);
+ if (status != NT_STATUS_SUCCESS) {
+ smb_node_end_crit(dst_fnode);
+ smb_rename_release_src(sr);
+ smb_node_release(dst_fnode);
+ smb_node_release(dst_dnode);
+ return (NT_STATUS_ACCESS_DENIED);
+ }
+
+ new_name = dst_fnode->od_name;
+ }
+
+ rc = smb_fsop_rename(sr, sr->user_cr,
+ src_dnode, src_fnode->od_name,
+ dst_dnode, new_name);
+
+ if (rc == 0) {
+ /*
+ * Note that renames in the same directory are normally
+ * delivered in {old,new} pairs, and clients expect them
+ * in that order, if both events are delivered.
+ */
+ int a_src, a_dst; /* action codes */
+ if (src_dnode == dst_dnode) {
+ a_src = FILE_ACTION_RENAMED_OLD_NAME;
+ a_dst = FILE_ACTION_RENAMED_NEW_NAME;
+ } else {
+ a_src = FILE_ACTION_REMOVED;
+ a_dst = FILE_ACTION_ADDED;
+ }
+ smb_node_notify_change(src_dnode, a_src, src_fnode->od_name);
+ smb_node_notify_change(dst_dnode, a_dst, new_name);
+ }
+
+ smb_rename_release_src(sr);
+
+ if (dst_fqi->fq_fnode) {
+ smb_node_end_crit(dst_fnode);
+ smb_node_release(dst_fnode);
+ }
+ smb_node_release(dst_dnode);
+
+ return (smb_rename_errno2status(rc));
+}
+
+/*
+ * smb_rename_check_stream
+ *
+ * For a stream rename the dst path must begin with ':', or "\\:".
+ * We don't yet support stream rename, Return EACCES.
+ *
+ * If not a stream rename, in accordance with the above rule,
+ * it is not valid for either the src or dst to be a stream.
+ * Return EINVAL.
+ */
+static int
+smb_rename_check_stream(smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
+{
+ smb_node_t *src_fnode = src_fqi->fq_fnode;
+ char *src_path = src_fqi->fq_path.pn_path;
+ char *dst_path = dst_fqi->fq_path.pn_path;
+
+ /* We do not yet support named stream rename - ACCESS DENIED */
+ if ((dst_path[0] == ':') ||
+ ((dst_path[0] == '\\') && (dst_path[1] == ':'))) {
+ return (EACCES);
+ }
+
+ /*
+ * If not stream rename (above) neither src or dst can be
+ * a named stream.
+ */
+
+ if (smb_is_stream_name(dst_path))
+ return (EINVAL);
+
+ if (src_fqi->fq_fnode) {
+ if (SMB_IS_STREAM(src_fnode))
+ return (EINVAL);
+ } else {
+ if (smb_is_stream_name(src_path))
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+
+/*
+ * smb_setinfo_link
+ *
+ * Implements FileRenameInformation for SMB1 Trans2 setinfo, SMB2 setinfo.
+ * If the new filename (dst_fqi) already exists it may be overwritten
+ * if flags == 1.
+ *
+ * The passed path is a full path relative to the share root.
+ *
+ * Returns NT status codes.
+ *
+ * Similar to smb_setinfo_rename(), above.
+ */
+uint32_t
+smb_setinfo_link(smb_request_t *sr, smb_node_t *node, char *path, int flags)
+{
+ smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
+ smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
+ smb_pathname_t *dst_pn = &dst_fqi->fq_path;
+ uint32_t status;
+
+ sr->arg.dirop.flags = flags ? SMB_RENAME_FLAG_OVERWRITE : 0;
+ sr->arg.dirop.info_level = FileLinkInformation;
+
+ src_fqi->fq_sattr = SMB_SEARCH_ATTRIBUTES;
+ src_fqi->fq_fnode = node;
+ src_fqi->fq_dnode = node->n_dnode;
+
+ /* validate the dst pathname */
+ smb_pathname_init(sr, dst_pn, path);
+ if (!smb_pathname_validate(sr, dst_pn))
+ return (NT_STATUS_OBJECT_NAME_INVALID);
+
+ status = smb_make_link(sr, src_fqi, dst_fqi);
+ return (status);
+}
+
+/*
+ * smb_make_link
+ *
+ * Creating a hard link (adding an additional name) for a file.
+ *
+ * If the source and destination are identical, we go through all
+ * the checks but we don't create a link.
+ *
+ * If the file is a symlink we create the hardlink on the target
+ * of the symlink (i.e. use SMB_FOLLOW_LINKS when looking up src).
+ * If the target of the symlink does not exist we fail with ENOENT.
+ *
+ * Returns NT status values.
+ *
+ * Similar to smb_common_rename() above.
+ */
+uint32_t
+smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
+{
+ smb_node_t *tnode;
+ char *path;
+ int rc;
+
+ tnode = sr->tid_tree->t_snode;
+ path = dst_fqi->fq_path.pn_path;
+
+ /* Cannnot create link on named stream */
+ if (smb_is_stream_name(src_fqi->fq_path.pn_path) ||
+ smb_is_stream_name(dst_fqi->fq_path.pn_path)) {
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ /* The source node may already have been provided */
+ if (src_fqi->fq_fnode) {
+ smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
+ smb_node_ref(src_fqi->fq_fnode);
+ smb_node_ref(src_fqi->fq_dnode);
+ } else {
+ /* lookup and validate src node */
+ rc = smb_rename_lookup_src(sr);
+ if (rc != 0)
+ return (smb_rename_errno2status(rc));
+ }
+
+ /* Not valid to create hardlink for directory */
+ if (smb_node_is_dir(src_fqi->fq_fnode)) {
+ smb_rename_release_src(sr);
+ return (NT_STATUS_FILE_IS_A_DIRECTORY);
+ }
+
+ /*
+ * Find the destination dnode and last component.
+ * May already be provided, i.e. when called via
+ * SMB1 trans2 setinfo.
+ */
+ if (dst_fqi->fq_dnode) {
+ smb_node_ref(dst_fqi->fq_dnode);
+ } else {
+ rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
+ &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
+ if (rc != 0) {
+ smb_rename_release_src(sr);
+ return (smb_rename_errno2status(rc));
+ }
+ }
+
+ /* If CI name match in same directory, we're done */
+ if ((src_fqi->fq_dnode == dst_fqi->fq_dnode) &&
+ (smb_strcasecmp(src_fqi->fq_fnode->od_name,
+ dst_fqi->fq_last_comp, 0) == 0)) {
+ smb_rename_release_src(sr);
+ smb_node_release(dst_fqi->fq_dnode);
+ return (0);
+ }
+
+ if (smb_is_invalid_filename(dst_fqi->fq_last_comp)) {
+ smb_rename_release_src(sr);
+ smb_node_release(dst_fqi->fq_dnode);
+ return (NT_STATUS_OBJECT_NAME_INVALID);
+ }
+
+ /* Lookup the destination node. It MUST NOT exist. */
+ rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
+ dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode);
+ if (rc == 0) {
+ smb_node_release(dst_fqi->fq_fnode);
+ rc = EEXIST;
+ }
+ if (rc != ENOENT) {
+ smb_rename_release_src(sr);
+ smb_node_release(dst_fqi->fq_dnode);
+ return (smb_rename_errno2status(rc));
+ }
+
+ rc = smb_fsop_link(sr, sr->user_cr, src_fqi->fq_fnode,
+ dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
+
+ if (rc == 0) {
+ smb_node_notify_change(dst_fqi->fq_dnode,
+ FILE_ACTION_ADDED, dst_fqi->fq_last_comp);
+ }
+
+ smb_rename_release_src(sr);
+ smb_node_release(dst_fqi->fq_dnode);
+ return (smb_rename_errno2status(rc));
+}
+
+/*
+ * smb_rename_lookup_src
+ *
+ * Lookup the src node, checking for sharing violations and
+ * breaking any existing BATCH oplock.
+ * Populate sr->arg.dirop.fqi
+ *
+ * Upon success, the dnode and fnode will have holds and the
+ * fnode will be in a critical section. These should be
+ * released using smb_rename_release_src().
+ *
+ * Returns errno values.
+ */
+static int
+smb_rename_lookup_src(smb_request_t *sr)
+{
+ smb_node_t *src_node, *tnode;
+ DWORD status;
+ int rc;
+ int count;
+ char *path;
+
+ smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
+
+ if (smb_is_stream_name(src_fqi->fq_path.pn_path))
+ return (EINVAL);
+
+ /* Lookup the source node */
+ tnode = sr->tid_tree->t_snode;
+ path = src_fqi->fq_path.pn_path;
+ rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
+ &src_fqi->fq_dnode, src_fqi->fq_last_comp);
+ if (rc != 0)
+ return (rc);
+
+ rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
+ src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode);
+ if (rc != 0) {
+ smb_node_release(src_fqi->fq_dnode);
+ return (rc);
+ }
+ src_node = src_fqi->fq_fnode;
+
+ rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr);
+ if (rc != 0) {
+ smb_node_release(src_fqi->fq_fnode);
+ smb_node_release(src_fqi->fq_dnode);
+ return (rc);
+ }
+
+ /*
+ * Break BATCH oplock before ofile checks. If a client
+ * has a file open, this will force a flush or close,
+ * which may affect the outcome of any share checking.
+ */
+ (void) smb_oplock_break(sr, src_node,
+ SMB_OPLOCK_BREAK_TO_LEVEL_II | SMB_OPLOCK_BREAK_BATCH);
+
+ /*
+ * Wait (a little) for the oplock break to be
+ * responded to by clients closing handles.
+ * Hold node->n_lock as reader to keep new
+ * ofiles from showing up after we check.
+ */
+ smb_node_rdlock(src_node);
+ for (count = 0; count <= 12; count++) {
+ status = smb_node_rename_check(src_node);
+ if (status != NT_STATUS_SHARING_VIOLATION)
+ break;
+ smb_node_unlock(src_node);
+ delay(MSEC_TO_TICK(100));
+ smb_node_rdlock(src_node);
+ }
+ if (status != NT_STATUS_SUCCESS) {
+ smb_node_unlock(src_node);
+ smb_node_release(src_fqi->fq_fnode);
+ smb_node_release(src_fqi->fq_dnode);
+ return (EPIPE); /* = ERRbadshare */
+ }
+
+ /*
+ * Note, the combination of these two:
+ * smb_node_rdlock(node);
+ * nbl_start_crit(node->vp, RW_READER);
+ * is equivalent to this call:
+ * smb_node_start_crit(node, RW_READER)
+ *
+ * Cleanup after this point should use:
+ * smb_node_end_crit(src_node)
+ */
+ nbl_start_crit(src_node->vp, RW_READER);
+
+ /*
+ * This checks nbl_share_conflict, nbl_lock_conflict
+ */
+ status = smb_nbl_conflict(src_node, 0, UINT64_MAX, NBL_RENAME);
+ if (status != NT_STATUS_SUCCESS) {
+ smb_node_end_crit(src_node);
+ smb_node_release(src_fqi->fq_fnode);
+ smb_node_release(src_fqi->fq_dnode);
+ if (status == NT_STATUS_SHARING_VIOLATION)
+ return (EPIPE); /* = ERRbadshare */
+ return (EACCES);
+ }
+
+ /* NB: Caller expects holds on src_fqi fnode, dnode */
+ return (0);
+}
+
+/*
+ * smb_rename_release_src
+ */
+static void
+smb_rename_release_src(smb_request_t *sr)
+{
+ smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
+
+ smb_node_end_crit(src_fqi->fq_fnode);
+ smb_node_release(src_fqi->fq_fnode);
+ smb_node_release(src_fqi->fq_dnode);
+}
+
+
+static int
+smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr)
+{
+ smb_attr_t attr;
+
+ bzero(&attr, sizeof (attr));
+ attr.sa_mask = SMB_AT_DOSATTR;
+ if (smb_node_getattr(sr, node, zone_kcred(), NULL, &attr) != 0)
+ return (EACCES);
+
+ if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) &&
+ !(SMB_SEARCH_HIDDEN(sattr)))
+ return (ESRCH);
+
+ if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) &&
+ !(SMB_SEARCH_SYSTEM(sattr)))
+ return (ESRCH);
+
+ return (0);
+}
+
+/*
+ * The following values are based on observed WFWG, Windows 9x, Windows NT
+ * and Windows 2000 behaviour.
+ *
+ * ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
+ *
+ * Windows 95 clients don't see the problem because the target is deleted
+ * before the rename request.
+ */
+static uint32_t
+smb_rename_errno2status(int errnum)
+{
+ static struct {
+ int errnum;
+ uint32_t status32;
+ } rc_map[] = {
+ { EEXIST, NT_STATUS_OBJECT_NAME_COLLISION },
+ { EPIPE, NT_STATUS_SHARING_VIOLATION },
+ { ENOENT, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { ESRCH, NT_STATUS_NO_SUCH_FILE },
+ { EINVAL, NT_STATUS_INVALID_PARAMETER },
+ { EACCES, NT_STATUS_ACCESS_DENIED },
+ { EISDIR, NT_STATUS_FILE_IS_A_DIRECTORY },
+ { EIO, NT_STATUS_INTERNAL_ERROR }
+ };
+
+ int i;
+
+ if (errnum == 0)
+ return (0);
+
+ for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) {
+ if (rc_map[i].errnum == errnum) {
+ return (rc_map[i].status32);
+ }
+ }
+
+ return (smb_errno2status(errnum));
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_cmn_setfile.c b/usr/src/uts/common/fs/smbsrv/smb_cmn_setfile.c
new file mode 100644
index 0000000000..c3a4685680
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb_cmn_setfile.c
@@ -0,0 +1,231 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Common functions supporting both:
+ * SMB1 Trans2 Set File/Path Info,
+ * SMB2 Set File Info
+ */
+
+#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb_fsops.h>
+
+/*
+ * smb_set_basic_info
+ * [MS-FSCC] 2.4.7
+ * FileBasicInformation
+ * SMB_SET_FILE_BASIC_INFO
+ * SMB_FILE_BASIC_INFORMATION
+ *
+ * Sets basic file/path information.
+ *
+ * It is not valid to set FILE_ATTRIBUTE_DIRECTORY if the
+ * target is not a directory.
+ *
+ * For compatibility with windows servers:
+ * - if the specified attributes have ONLY FILE_ATTRIBUTE_NORMAL set
+ * clear (0) the file's attributes.
+ * - if the specified attributes are 0 do NOT change the file's attributes.
+ */
+uint32_t
+smb_set_basic_info(smb_request_t *sr, smb_setinfo_t *si)
+{
+ smb_attr_t *attr = &si->si_attr;
+ smb_node_t *node = si->si_node;
+ uint64_t crtime, atime, mtime, ctime;
+ uint32_t attributes;
+ int rc;
+
+ if (smb_mbc_decodef(&si->si_data, "qqqql",
+ &crtime, &atime, &mtime, &ctime, &attributes) != 0)
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
+
+ if ((attributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ (!smb_node_is_dir(node)))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ bzero(attr, sizeof (*attr));
+ if (atime != 0 && atime != (uint64_t)-1) {
+ smb_time_nt_to_unix(atime, &attr->sa_vattr.va_atime);
+ attr->sa_mask |= SMB_AT_ATIME;
+ }
+ if (mtime != 0 && mtime != (uint64_t)-1) {
+ smb_time_nt_to_unix(mtime, &attr->sa_vattr.va_mtime);
+ attr->sa_mask |= SMB_AT_MTIME;
+ }
+ if (ctime != 0 && ctime != (uint64_t)-1) {
+ smb_time_nt_to_unix(ctime, &attr->sa_vattr.va_ctime);
+ attr->sa_mask |= SMB_AT_CTIME;
+ }
+ if (crtime != 0 && crtime != (uint64_t)-1) {
+ smb_time_nt_to_unix(crtime, &attr->sa_crtime);
+ attr->sa_mask |= SMB_AT_CRTIME;
+ }
+
+ if (attributes != 0) {
+ attr->sa_dosattr = attributes;
+ attr->sa_mask |= SMB_AT_DOSATTR;
+ }
+
+ rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, attr);
+ if (rc != 0)
+ return (smb_errno2status(rc));
+
+ return (0);
+}
+
+/*
+ * smb_set_eof_info
+ * FileEndOfFileInformation
+ * SMB_SET_FILE_END_OF_FILE_INFO
+ * SMB_FILE_END_OF_FILE_INFORMATION
+ */
+uint32_t
+smb_set_eof_info(smb_request_t *sr, smb_setinfo_t *si)
+{
+ smb_attr_t *attr = &si->si_attr;
+ smb_node_t *node = si->si_node;
+ uint64_t eof;
+ int rc;
+
+ if (smb_mbc_decodef(&si->si_data, "q", &eof) != 0)
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
+
+ if (smb_node_is_dir(node))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ /* If opened by path, break exclusive oplock */
+ if (sr->fid_ofile == NULL)
+ (void) smb_oplock_break(sr, node,
+ SMB_OPLOCK_BREAK_EXCLUSIVE | SMB_OPLOCK_BREAK_TO_NONE);
+
+ bzero(attr, sizeof (*attr));
+ attr->sa_mask = SMB_AT_SIZE;
+ attr->sa_vattr.va_size = (u_offset_t)eof;
+ rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, attr);
+ if (rc != 0)
+ return (smb_errno2status(rc));
+
+ smb_oplock_break_levelII(node);
+ return (0);
+}
+
+/*
+ * smb_set_alloc_info
+ * FileAllocationInformation
+ * SMB_SET_FILE_ALLOCATION_INFO
+ * SMB_FILE_ALLOCATION_INFORMATION
+ */
+uint32_t
+smb_set_alloc_info(smb_request_t *sr, smb_setinfo_t *si)
+{
+ smb_attr_t *attr = &si->si_attr;
+ smb_node_t *node = si->si_node;
+ uint64_t allocsz;
+ int rc;
+
+ if (smb_mbc_decodef(&si->si_data, "q", &allocsz) != 0)
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
+
+ if (smb_node_is_dir(node))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ /* If opened by path, break exclusive oplock */
+ if (sr->fid_ofile == NULL)
+ (void) smb_oplock_break(sr, node,
+ SMB_OPLOCK_BREAK_EXCLUSIVE | SMB_OPLOCK_BREAK_TO_NONE);
+
+ bzero(attr, sizeof (*attr));
+ attr->sa_mask = SMB_AT_ALLOCSZ;
+ attr->sa_allocsz = (u_offset_t)allocsz;
+ rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, attr);
+ if (rc != 0)
+ return (smb_errno2status(rc));
+
+ smb_oplock_break_levelII(node);
+ return (0);
+}
+
+/*
+ * smb_set_disposition_info
+ * See:
+ * FileDispositionInformation
+ * SMB_SET_FILE_DISPOSITION_INFO
+ * SMB_FILE_DISPOSITION_INFORMATION
+ *
+ * Set/Clear DELETE_ON_CLOSE flag for an open file.
+ * File should have been opened with DELETE access otherwise
+ * the operation is not permitted.
+ *
+ * NOTE: The node should be marked delete-on-close upon the receipt
+ * of the Trans2SetFileInfo(SetDispositionInfo) if mark_delete is set.
+ * It is different than both SmbNtCreateAndX and SmbNtTransact, which
+ * set delete-on-close on the ofile and defer setting the flag on the
+ * node until the file is closed.
+ *
+ * Observation of Windows 2000 indicates the following:
+ *
+ * 1) If a file is not opened with delete-on-close create options and
+ * the delete-on-close is set via Trans2SetFileInfo(SetDispositionInfo)
+ * using that open file handle, any subsequent open requests will fail
+ * with DELETE_PENDING.
+ *
+ * 2) If a file is opened with delete-on-close create options and the
+ * client attempts to unset delete-on-close via Trans2SetFileInfo
+ * (SetDispositionInfo) prior to the file close, any subsequent open
+ * requests will still fail with DELETE_PENDING after the file is closed.
+ *
+ * 3) If a file is opened with delete-on-close create options and that
+ * file handle (not the last open handle and the only file handle
+ * with delete-on-close set) is closed. Any subsequent open requests
+ * will fail with DELETE_PENDING. Unsetting delete-on-close via
+ * Trans2SetFileInfo(SetDispositionInfo) at this time will unset the
+ * node delete-on-close flag, which will result in the file not being
+ * removed even after the last file handle is closed.
+ */
+uint32_t
+smb_set_disposition_info(smb_request_t *sr, smb_setinfo_t *si)
+{
+ smb_node_t *node = si->si_node;
+ smb_ofile_t *of = sr->fid_ofile;
+ uint8_t mark_delete;
+ uint32_t flags = 0;
+
+ if (smb_mbc_decodef(&si->si_data, "b", &mark_delete) != 0)
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
+
+ if ((of == NULL) || !(smb_ofile_granted_access(of) & DELETE))
+ return (NT_STATUS_ACCESS_DENIED);
+
+ if (mark_delete) {
+ if (SMB_TREE_SUPPORTS_CATIA(sr))
+ flags |= SMB_CATIA;
+ return (smb_node_set_delete_on_close(node, of->f_cr, flags));
+ } else {
+ smb_node_reset_delete_on_close(node);
+ }
+
+ return (NT_STATUS_SUCCESS);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_open.c b/usr/src/uts/common/fs/smbsrv/smb_common_open.c
index ebd4169853..780f805cae 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_common_open.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_common_open.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2007, 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.
*/
/*
@@ -61,15 +61,11 @@ static boolean_t smb_open_overwrite(smb_arg_open_t *);
* FILE_WRITE_ATTRIBUTES, FILE_WRITE_EA, and FILE_APPEND_DATA
*
* GENERIC_EXECUTE STANDARD_RIGHTS_EXECUTE, SYNCHRONIZE, and FILE_EXECUTE.
- *
- * Careful, we have to emulate some Windows behavior here.
- * When requested access == zero, you get READ_CONTROL.
- * MacOS 10.7 depends on this.
*/
-uint32_t
+static uint32_t
smb_access_generic_to_file(uint32_t desired_access)
{
- uint32_t access = READ_CONTROL;
+ uint32_t access = 0;
if (desired_access & GENERIC_ALL)
return (FILE_ALL_ACCESS & ~SYNCHRONIZE);
@@ -202,15 +198,8 @@ smb_common_open(smb_request_t *sr)
bcopy(parg, &sr->arg.open, sizeof (*parg));
}
- if (status == NT_STATUS_SHARING_VIOLATION) {
- smbsr_error(sr, NT_STATUS_SHARING_VIOLATION,
- ERRDOS, ERROR_SHARING_VIOLATION);
- }
-
- if (status == NT_STATUS_NO_SUCH_FILE) {
- smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
- ERRDOS, ERROR_FILE_NOT_FOUND);
- }
+ if (status == NT_STATUS_NO_SUCH_FILE)
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
kmem_free(parg, sizeof (*parg));
return (status);
@@ -234,9 +223,7 @@ smb_common_open(smb_request_t *sr)
* parameters to the node. We test the omode write-through flag in all
* write functions.
*
- * This function will return NT status codes but it also raises errors,
- * in which case it won't return to the caller. Be careful how you
- * handle things in here.
+ * This function returns NT status codes.
*
* The following rules apply when processing a file open request:
*
@@ -334,8 +321,6 @@ smb_open_subr(smb_request_t *sr)
if ((op->create_disposition != FILE_CREATE) &&
(op->create_disposition != FILE_OPEN_IF) &&
(op->create_disposition != FILE_OPEN)) {
- smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
- ERRDOS, ERROR_INVALID_ACCESS);
return (NT_STATUS_INVALID_PARAMETER);
}
}
@@ -350,9 +335,6 @@ smb_open_subr(smb_request_t *sr)
ASSERT(sr->uid_user);
cmn_err(CE_NOTE, "smbsrv[%s\\%s]: TOO_MANY_OPENED_FILES",
sr->uid_user->u_domain, sr->uid_user->u_name);
-
- smbsr_error(sr, NT_STATUS_TOO_MANY_OPENED_FILES,
- ERRDOS, ERROR_TOO_MANY_OPEN_FILES);
return (NT_STATUS_TOO_MANY_OPENED_FILES);
}
@@ -367,10 +349,19 @@ smb_open_subr(smb_request_t *sr)
break;
case STYPE_IPC:
+ /*
+ * Security descriptors for pipes are not implemented,
+ * so just setup a reasonable access mask.
+ */
+ op->desired_access = (READ_CONTROL | SYNCHRONIZE |
+ FILE_READ_DATA | FILE_READ_ATTRIBUTES |
+ FILE_WRITE_DATA | FILE_APPEND_DATA);
+ /*
+ * Limit the number of open pipe instances.
+ */
if ((rc = smb_threshold_enter(&sv->sv_opipe_ct)) != 0) {
status = RPC_NT_SERVER_TOO_BUSY;
- smbsr_error(sr, status, 0, 0);
return (status);
}
@@ -380,15 +371,10 @@ smb_open_subr(smb_request_t *sr)
*/
uniq_fid = SMB_UNIQ_FID();
status = smb_opipe_open(sr, uniq_fid);
- if (status != NT_STATUS_SUCCESS)
- smbsr_error(sr, status, 0, 0);
-
smb_threshold_exit(&sv->sv_opipe_ct);
return (status);
default:
- smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE,
- ERRDOS, ERROR_BAD_DEV_TYPE);
return (NT_STATUS_BAD_DEVICE_TYPE);
}
@@ -397,8 +383,7 @@ smb_open_subr(smb_request_t *sr)
return (sr->smb_error.status);
if (strlen(pn->pn_path) >= SMB_MAXPATHLEN) {
- smbsr_error(sr, 0, ERRSRV, ERRfilespecs);
- return (NT_STATUS_NAME_TOO_LONG);
+ return (NT_STATUS_OBJECT_PATH_INVALID);
}
if (is_dir) {
@@ -424,12 +409,8 @@ smb_open_subr(smb_request_t *sr)
*/
if (cur_node == sr->tid_tree->t_snode) {
if (op->create_disposition == FILE_OPEN) {
- smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
- ERRDOS, ERROR_FILE_NOT_FOUND);
return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
}
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
- ERROR_ACCESS_DENIED);
return (NT_STATUS_ACCESS_DENIED);
}
@@ -444,8 +425,7 @@ smb_open_subr(smb_request_t *sr)
sr->tid_tree->t_snode, cur_node, &op->fqi.fq_dnode,
op->fqi.fq_last_comp);
if (rc != 0) {
- smbsr_errno(sr, rc);
- return (sr->smb_error.status);
+ return (smb_errno2status(rc));
}
}
@@ -475,9 +455,7 @@ smb_open_subr(smb_request_t *sr)
if (rc != 0) {
smb_node_release(op->fqi.fq_fnode);
smb_node_release(op->fqi.fq_dnode);
- smbsr_error(sr, NT_STATUS_INTERNAL_ERROR,
- ERRDOS, ERROR_INTERNAL_ERROR);
- return (sr->smb_error.status);
+ return (NT_STATUS_INTERNAL_ERROR);
}
} else if (rc == ENOENT) {
last_comp_found = B_FALSE;
@@ -485,8 +463,7 @@ smb_open_subr(smb_request_t *sr)
rc = 0;
} else {
smb_node_release(op->fqi.fq_dnode);
- smbsr_errno(sr, rc);
- return (sr->smb_error.status);
+ return (smb_errno2status(rc));
}
@@ -507,8 +484,6 @@ smb_open_subr(smb_request_t *sr)
!smb_node_is_symlink(node)) {
smb_node_release(node);
smb_node_release(dnode);
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
- ERRnoaccess);
return (NT_STATUS_ACCESS_DENIED);
}
@@ -523,8 +498,6 @@ smb_open_subr(smb_request_t *sr)
if (op->create_options & FILE_NON_DIRECTORY_FILE) {
smb_node_release(node);
smb_node_release(dnode);
- smbsr_error(sr, NT_STATUS_FILE_IS_A_DIRECTORY,
- ERRDOS, ERROR_ACCESS_DENIED);
return (NT_STATUS_FILE_IS_A_DIRECTORY);
}
} else {
@@ -532,8 +505,6 @@ smb_open_subr(smb_request_t *sr)
(op->nt_flags & NT_CREATE_FLAG_OPEN_TARGET_DIR)) {
smb_node_release(node);
smb_node_release(dnode);
- smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
- ERRDOS, ERROR_DIRECTORY);
return (NT_STATUS_NOT_A_DIRECTORY);
}
}
@@ -545,8 +516,6 @@ smb_open_subr(smb_request_t *sr)
if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
smb_node_release(node);
smb_node_release(dnode);
- smbsr_error(sr, NT_STATUS_DELETE_PENDING,
- ERRDOS, ERROR_ACCESS_DENIED);
return (NT_STATUS_DELETE_PENDING);
}
@@ -556,8 +525,6 @@ smb_open_subr(smb_request_t *sr)
if (op->create_disposition == FILE_CREATE) {
smb_node_release(node);
smb_node_release(dnode);
- smbsr_error(sr, NT_STATUS_OBJECT_NAME_COLLISION,
- ERRDOS, ERROR_FILE_EXISTS);
return (NT_STATUS_OBJECT_NAME_COLLISION);
}
@@ -575,8 +542,6 @@ smb_open_subr(smb_request_t *sr)
FILE_APPEND_DATA)) {
smb_node_release(node);
smb_node_release(dnode);
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
- ERRDOS, ERRnoaccess);
return (NT_STATUS_ACCESS_DENIED);
}
}
@@ -590,8 +555,6 @@ smb_open_subr(smb_request_t *sr)
op->dattr)) {
smb_node_release(node);
smb_node_release(dnode);
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
- ERRDOS, ERRnoaccess);
return (NT_STATUS_ACCESS_DENIED);
}
@@ -615,13 +578,10 @@ smb_open_subr(smb_request_t *sr)
smb_node_release(node);
smb_node_release(dnode);
+ /* SMB1 specific? NT_STATUS_PRIVILEGE_NOT_HELD */
if (status == NT_STATUS_PRIVILEGE_NOT_HELD) {
- smbsr_error(sr, status,
- ERRDOS, ERROR_PRIVILEGE_NOT_HELD);
return (status);
} else {
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
- ERRDOS, ERROR_ACCESS_DENIED);
return (NT_STATUS_ACCESS_DENIED);
}
}
@@ -630,6 +590,17 @@ smb_open_subr(smb_request_t *sr)
smb_fsop_eaccess(sr, sr->user_cr, node, &max_allowed);
op->desired_access |= max_allowed;
}
+ /*
+ * According to MS "dochelp" mail in Mar 2015, any handle
+ * on which read or write access is granted implicitly
+ * gets "read attributes", even if it was not requested.
+ * This avoids unexpected access failures later that
+ * would happen if these were not granted.
+ */
+ if ((op->desired_access & FILE_DATA_ALL) != 0) {
+ op->desired_access |= (READ_CONTROL |
+ FILE_READ_ATTRIBUTES);
+ }
/*
* Oplock break is done prior to sharing checks as the break
@@ -669,9 +640,15 @@ smb_open_subr(smb_request_t *sr)
op->dattr &= ~FILE_ATTRIBUTE_READONLY;
}
+ /*
+ * Truncate the file data here.
+ * We set alloc_size = op->dsize later,
+ * after we have an ofile. See:
+ * smb_set_open_attributes
+ */
bzero(&new_attr, sizeof (new_attr));
new_attr.sa_dosattr = op->dattr;
- new_attr.sa_vattr.va_size = op->dsize;
+ new_attr.sa_vattr.va_size = 0;
new_attr.sa_mask = SMB_AT_DOSATTR | SMB_AT_SIZE;
rc = smb_fsop_setattr(sr, sr->user_cr, node, &new_attr);
if (rc != 0) {
@@ -680,24 +657,23 @@ smb_open_subr(smb_request_t *sr)
smb_node_dec_opening_count(node);
smb_node_release(node);
smb_node_release(dnode);
- smbsr_errno(sr, rc);
- return (sr->smb_error.status);
+ return (smb_errno2status(rc));
}
/*
* If file is being replaced, remove existing streams
*/
if (SMB_IS_STREAM(node) == 0) {
- rc = smb_fsop_remove_streams(sr, sr->user_cr,
- node);
- if (rc != 0) {
+ status = smb_fsop_remove_streams(sr,
+ sr->user_cr, node);
+ if (status != 0) {
smb_fsop_unshrlock(sr->user_cr, node,
uniq_fid);
smb_node_unlock(node);
smb_node_dec_opening_count(node);
smb_node_release(node);
smb_node_release(dnode);
- return (sr->smb_error.status);
+ return (status);
}
}
@@ -708,6 +684,12 @@ smb_open_subr(smb_request_t *sr)
/*
* FILE_OPEN or FILE_OPEN_IF.
*/
+ /*
+ * Ignore any user-specified alloc_size for
+ * existing files, to avoid truncation in
+ * smb_set_open_attributes
+ */
+ op->dsize = 0L;
op->action_taken = SMB_OACT_OPENED;
break;
}
@@ -721,15 +703,11 @@ smb_open_subr(smb_request_t *sr)
if ((op->create_disposition == FILE_OPEN) ||
(op->create_disposition == FILE_OVERWRITE)) {
smb_node_release(dnode);
- smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
- ERRDOS, ERROR_FILE_NOT_FOUND);
return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
}
if (pn->pn_fname && smb_is_invalid_filename(pn->pn_fname)) {
smb_node_release(dnode);
- smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
- ERRDOS, ERROR_INVALID_NAME);
return (NT_STATUS_OBJECT_NAME_INVALID);
}
@@ -763,10 +741,11 @@ smb_open_subr(smb_request_t *sr)
new_attr.sa_mask |=
SMB_AT_DOSATTR | SMB_AT_TYPE | SMB_AT_MODE;
- if (op->dsize) {
- new_attr.sa_vattr.va_size = op->dsize;
- new_attr.sa_mask |= SMB_AT_SIZE;
- }
+ /*
+ * We set alloc_size = op->dsize later,
+ * after we have an ofile. See:
+ * smb_set_open_attributes
+ */
rc = smb_fsop_create(sr, sr->user_cr, dnode,
op->fqi.fq_last_comp, &new_attr, &op->fqi.fq_fnode);
@@ -774,8 +753,7 @@ smb_open_subr(smb_request_t *sr)
if (rc != 0) {
smb_node_unlock(dnode);
smb_node_release(dnode);
- smbsr_errno(sr, rc);
- return (sr->smb_error.status);
+ return (smb_errno2status(rc));
}
node = op->fqi.fq_fnode;
@@ -807,8 +785,7 @@ smb_open_subr(smb_request_t *sr)
if (rc != 0) {
smb_node_unlock(dnode);
smb_node_release(dnode);
- smbsr_errno(sr, rc);
- return (sr->smb_error.status);
+ return (smb_errno2status(rc));
}
node = op->fqi.fq_fnode;
@@ -823,6 +800,15 @@ smb_open_subr(smb_request_t *sr)
smb_fsop_eaccess(sr, sr->user_cr, node, &max_allowed);
op->desired_access |= max_allowed;
}
+ /*
+ * We created created this object (we own it) so
+ * grant read/write attributes on this handle,
+ * even if that was not requested. This avoids
+ * unexpected access failures later that would
+ * happen if these were not granted.
+ */
+ op->desired_access |= (READ_CONTROL |
+ FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES);
}
status = NT_STATUS_SUCCESS;
@@ -830,7 +816,6 @@ smb_open_subr(smb_request_t *sr)
of = smb_ofile_open(sr, node, op, SMB_FTYPE_DISK, uniq_fid,
&err);
if (of == NULL) {
- smbsr_error(sr, err.status, err.errcls, err.errcode);
status = err.status;
}
@@ -843,7 +828,6 @@ smb_open_subr(smb_request_t *sr)
*/
if (status == NT_STATUS_SUCCESS &&
!smb_tree_is_connected(sr->tid_tree)) {
- smbsr_error(sr, 0, ERRSRV, ERRinvnid);
status = NT_STATUS_INVALID_PARAMETER;
}
@@ -854,8 +838,7 @@ smb_open_subr(smb_request_t *sr)
*/
if (status == NT_STATUS_SUCCESS) {
if ((rc = smb_set_open_attributes(sr, of)) != 0) {
- smbsr_errno(sr, rc);
- status = sr->smb_error.status;
+ status = smb_errno2status(rc);
}
}
@@ -870,8 +853,6 @@ smb_open_subr(smb_request_t *sr)
rc = smb_node_getattr(sr, node, zone_kcred(), of,
&op->fqi.fq_fattr);
if (rc != 0) {
- smbsr_error(sr, NT_STATUS_INTERNAL_ERROR,
- ERRDOS, ERROR_INTERNAL_ERROR);
status = NT_STATUS_INTERNAL_ERROR;
}
}
@@ -1029,6 +1010,11 @@ smb_set_open_attributes(smb_request_t *sr, smb_ofile_t *of)
attr.sa_mask |= SMB_AT_DOSATTR;
}
+ if (op->dsize != 0) {
+ attr.sa_allocsz = op->dsize;
+ attr.sa_mask |= SMB_AT_ALLOCSZ;
+ }
+
if ((op->mtime.tv_sec != 0) && (op->mtime.tv_sec != UINT_MAX)) {
attr.sa_vattr.va_mtime = op->mtime;
attr.sa_mask |= SMB_AT_MTIME;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_transact.c b/usr/src/uts/common/fs/smbsrv/smb_common_transact.c
index 936d8b98c7..ffe230f888 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_common_transact.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_common_transact.c
@@ -30,6 +30,7 @@
#include <smbsrv/string.h>
#include <smbsrv/nmpipes.h>
#include <smbsrv/mailslot.h>
+#include <smbsrv/winioctl.h>
/*
* count of bytes in server response packet
@@ -76,6 +77,11 @@ smb_com_transaction(smb_request_t *sr)
char *stn;
int ready;
+ if (!STYPE_ISIPC(sr->tid_tree->t_res_type)) {
+ smbsr_error(sr, 0, ERRDOS, ERRnoaccess);
+ return (SDRC_ERROR);
+ }
+
rc = smbsr_decode_vwv(sr, SMB_TRANSHDR_ED_FMT,
&tpscnt, &tdscnt, &mprcnt, &mdrcnt, &msrcnt, &flags,
&timeo, &pscnt, &psoff, &dscnt, &dsoff, &suwcnt);
@@ -771,6 +777,8 @@ smb_encode_SHARE_INFO_2(struct mbuf_chain *output, struct mbuf_chain *text,
int
smb_trans_net_share_enum(struct smb_request *sr, struct smb_xa *xa)
{
+ uint16_t pid_hi, pid_lo;
+
/*
* Number of data bytes that will
* be sent in the current response
@@ -905,6 +913,9 @@ smb_trans_net_share_enum(struct smb_request *sr, struct smb_xa *xa)
tot_packet_bytes = param_pad + param_scnt + data_pad +
data_scnt;
+ pid_hi = sr->smb_pid >> 16;
+ pid_lo = (uint16_t)sr->smb_pid;
+
MBC_FLUSH(&reply);
(void) smb_mbc_encodef(&reply, SMB_HEADER_ED_FMT,
sr->first_smb_com,
@@ -913,10 +924,10 @@ smb_trans_net_share_enum(struct smb_request *sr, struct smb_xa *xa)
sr->smb_err,
sr->smb_flg | SMB_FLAGS_REPLY,
sr->smb_flg2,
- sr->smb_pid_high,
+ pid_hi,
sr->smb_sig,
sr->smb_tid,
- sr->smb_pid,
+ pid_lo,
sr->smb_uid,
sr->smb_mid);
@@ -1398,9 +1409,8 @@ is_supported_mailslot(const char *mailslot)
static smb_sdrc_t
smb_trans_nmpipe(smb_request_t *sr, smb_xa_t *xa)
{
- smb_vdb_t vdb;
- struct mbuf *mb;
- int rc;
+ smb_fsctl_t fsctl;
+ uint32_t status;
smbsr_lookup_file(sr);
if (sr->fid_ofile == NULL) {
@@ -1409,56 +1419,25 @@ smb_trans_nmpipe(smb_request_t *sr, smb_xa_t *xa)
return (SDRC_ERROR);
}
- rc = smb_mbc_decodef(&xa->req_data_mb, "#B",
- xa->smb_tdscnt, &vdb);
- if (rc != 0) {
- /* Not enough data sent. */
- smbsr_error(sr, 0, ERRSRV, ERRerror);
- return (SDRC_ERROR);
- }
-
- rc = smb_opipe_write(sr, &vdb.vdb_uio);
- if (rc != 0) {
- smbsr_errno(sr, rc);
- return (SDRC_ERROR);
- }
-
- vdb.vdb_tag = 0;
- vdb.vdb_uio.uio_iov = &vdb.vdb_iovec[0];
- vdb.vdb_uio.uio_iovcnt = MAX_IOVEC;
- vdb.vdb_uio.uio_segflg = UIO_SYSSPACE;
- vdb.vdb_uio.uio_extflg = UIO_COPY_DEFAULT;
- vdb.vdb_uio.uio_loffset = (offset_t)0;
- vdb.vdb_uio.uio_resid = xa->smb_mdrcnt;
- mb = smb_mbuf_allocate(&vdb.vdb_uio);
-
- rc = smb_opipe_read(sr, &vdb.vdb_uio);
- if (rc != 0) {
- m_freem(mb);
- smbsr_errno(sr, rc);
- return (SDRC_ERROR);
- }
-
- smb_mbuf_trim(mb, xa->smb_mdrcnt - vdb.vdb_uio.uio_resid);
- MBC_ATTACH_MBUF(&xa->rep_data_mb, mb);
-
/*
- * If the output buffer holds a partial pipe message,
- * we're supposed to return NT_STATUS_BUFFER_OVERFLOW.
- * As we don't have message boundary markers, the best
- * we can do is return that status when we have ALL of:
- * Output buffer was < SMB_PIPE_MAX_MSGSIZE
- * We filled the output buffer (resid==0)
- * There's more data (ioctl FIONREAD)
+ * A little confusing perhaps, but the fsctl "input" is what we
+ * write to the pipe (from the transaction "send" data), and the
+ * fsctl "output" is what we read from the pipe (and becomes the
+ * transaction receive data).
*/
- if (xa->smb_mdrcnt < SMB_PIPE_MAX_MSGSIZE &&
- vdb.vdb_uio.uio_resid == 0) {
- int nread = 0;
- rc = smb_opipe_get_nread(sr, &nread);
- if (rc == 0 && nread != 0) {
- smbsr_status(sr, NT_STATUS_BUFFER_OVERFLOW,
- ERRDOS, ERROR_MORE_DATA);
- }
+ fsctl.CtlCode = FSCTL_PIPE_TRANSCEIVE;
+ fsctl.InputCount = xa->smb_tdscnt; /* write count */
+ fsctl.OutputCount = 0; /* minimum to read from the pipe */
+ fsctl.MaxOutputResp = xa->smb_mdrcnt; /* max to read */
+ fsctl.in_mbc = &xa->req_data_mb; /* write from here */
+ fsctl.out_mbc = &xa->rep_data_mb; /* read into here */
+
+ status = smb_opipe_fsctl(sr, &fsctl);
+ if (status) {
+ smbsr_status(sr, status, 0, 0);
+ if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_ERROR)
+ return (SDRC_ERROR);
+ /* Warnings like NT_STATUS_BUFFER_OVERFLOW are OK */
}
return (SDRC_SUCCESS);
@@ -1475,7 +1454,7 @@ smb_trans_dispatch(smb_request_t *sr, smb_xa_t *xa)
char *req_fmt;
char *rep_fmt;
- if (xa->smb_suwcnt > 0 && STYPE_ISIPC(sr->tid_tree->t_res_type)) {
+ if (xa->smb_suwcnt > 0) {
rc = smb_mbc_decodef(&xa->req_setup_mb, "ww", &opcode,
&sr->smb_fid);
if (rc != 0)
@@ -2051,7 +2030,7 @@ smb_xa_complete(smb_xa_t *xa)
smb_xa_t *
smb_xa_find(
smb_session_t *session,
- uint16_t pid,
+ uint32_t pid,
uint16_t mid)
{
smb_xa_t *xa;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_create.c b/usr/src/uts/common/fs/smbsrv/smb_create.c
index 0d98a30bbb..f964ecfcff 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_create.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_create.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
@@ -207,5 +208,8 @@ smb_common_create(smb_request_t *sr)
~(SMB_FLAGS_OPLOCK | SMB_FLAGS_OPLOCK_NOTIFY_ANY);
}
+ if (status)
+ smbsr_status(sr, status, 0, 0);
+
return (status);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_delete.c b/usr/src/uts/common/fs/smbsrv/smb_delete.c
index 96435a0b81..3247ceb9c2 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_delete.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_delete.c
@@ -282,11 +282,11 @@ smb_delete_single_file(smb_request_t *sr, smb_error_t *err)
static int
smb_delete_multiple_files(smb_request_t *sr, smb_error_t *err)
{
- int rc, deleted = 0;
+ char namebuf[MAXNAMELEN];
smb_fqi_t *fqi;
- uint16_t odid;
smb_odir_t *od;
- char namebuf[MAXNAMELEN];
+ uint32_t status;
+ int rc, deleted = 0;
fqi = &sr->arg.dirop.fqi;
@@ -294,13 +294,12 @@ smb_delete_multiple_files(smb_request_t *sr, smb_error_t *err)
* Specify all search attributes (SMB_SEARCH_ATTRIBUTES) so that
* delete-specific checking can be done (smb_delete_check_dosattr).
*/
- odid = smb_odir_open(sr, fqi->fq_path.pn_path,
- SMB_SEARCH_ATTRIBUTES, 0);
- if (odid == 0)
- return (-1);
-
- if ((od = smb_tree_lookup_odir(sr, odid)) == NULL)
+ status = smb_odir_openpath(sr, fqi->fq_path.pn_path,
+ SMB_SEARCH_ATTRIBUTES, 0, &od);
+ if (status != 0) {
+ err->status = status;
return (-1);
+ }
for (;;) {
rc = smb_delete_find_fname(sr, od, namebuf, MAXNAMELEN);
@@ -379,10 +378,7 @@ smb_delete_find_fname(smb_request_t *sr, smb_odir_t *od, char *namebuf, int len)
rc = smb_odir_read(sr, od, odirent, &eos);
if (rc == 0) {
- if (eos)
- rc = ENOENT;
- else
- (void) strlcpy(namebuf, odirent->od_name, len);
+ (void) strlcpy(namebuf, odirent->od_name, len);
}
kmem_free(odirent, sizeof (smb_odirent_t));
return (rc);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_dfs.c b/usr/src/uts/common/fs/smbsrv/smb_dfs.c
new file mode 100644
index 0000000000..495965859b
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb_dfs.c
@@ -0,0 +1,541 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb_dfs.h>
+#include <smbsrv/smb_door.h>
+#include <smbsrv/winioctl.h>
+
+/*
+ * Get Referral response header flags
+ * For exact meaning refer to MS-DFSC spec.
+ *
+ * R: ReferralServers
+ * S: StorageServers
+ * T: TargetFailback
+ */
+#define DFS_HDRFLG_R 0x00000001
+#define DFS_HDRFLG_S 0x00000002
+#define DFS_HDRFLG_T 0x00000004
+
+/*
+ * Entry flags
+ */
+#define DFS_ENTFLG_T 0x0004
+
+/*
+ * Referral entry types/versions
+ */
+#define DFS_REFERRAL_V1 0x0001
+#define DFS_REFERRAL_V2 0x0002
+#define DFS_REFERRAL_V3 0x0003
+#define DFS_REFERRAL_V4 0x0004
+
+/*
+ * Valid values for ServerType field in referral entries
+ */
+#define DFS_SRVTYPE_NONROOT 0x0000
+#define DFS_SRVTYPE_ROOT 0x0001
+
+/*
+ * Size of the fix part for each referral entry type
+ */
+#define DFS_REFV1_ENTSZ 8
+#define DFS_REFV2_ENTSZ 22
+#define DFS_REFV3_ENTSZ 34
+#define DFS_REFV4_ENTSZ 34
+
+static dfs_reftype_t smb_dfs_get_reftype(const char *);
+static void smb_dfs_encode_hdr(mbuf_chain_t *, dfs_info_t *);
+static uint32_t smb_dfs_encode_refv1(smb_request_t *, mbuf_chain_t *,
+ dfs_info_t *);
+static uint32_t smb_dfs_encode_refv2(smb_request_t *, mbuf_chain_t *,
+ dfs_info_t *);
+static uint32_t smb_dfs_encode_refv3x(smb_request_t *, mbuf_chain_t *,
+ dfs_info_t *, uint16_t);
+static void smb_dfs_encode_targets(mbuf_chain_t *, dfs_info_t *);
+static uint32_t smb_dfs_referrals_get(smb_request_t *, char *, dfs_reftype_t,
+ dfs_referral_response_t *);
+static void smb_dfs_referrals_free(dfs_referral_response_t *);
+static uint16_t smb_dfs_referrals_unclen(dfs_info_t *, uint16_t);
+
+/*
+ * Note: SMB1 callers in smb_trans2_dfs.c
+ * smb_com_trans2_report_dfs_inconsistency
+ * smb_com_trans2_get_dfs_referral
+ */
+
+/*
+ * See [MS-DFSC] for details about this command
+ */
+uint32_t
+smb_dfs_get_referrals(smb_request_t *sr, smb_fsctl_t *fsctl)
+{
+ dfs_info_t *referrals;
+ dfs_referral_response_t refrsp;
+ dfs_reftype_t reftype;
+ char *path;
+ uint16_t maxver;
+ uint32_t status;
+ int rc;
+
+ /*
+ * The caller checks this, because the error reporting method
+ * varies across SMB versions.
+ */
+ ASSERT(STYPE_ISIPC(sr->tid_tree->t_res_type));
+
+ /*
+ * XXX Instead of decoding the referral request and encoding
+ * the response here (in-kernel) we could pass the given
+ * request buffer in our door call, and let that return the
+ * response buffer ready to stuff into out_mbc. That would
+ * allow all this decoding/encoding to happen at user-level.
+ * (and most of this file would go away. :-)
+ */
+ switch (fsctl->CtlCode) {
+ case FSCTL_DFS_GET_REFERRALS:
+ /*
+ * Input data is (w) MaxReferralLevel, (U) path
+ */
+ rc = smb_mbc_decodef(fsctl->in_mbc, "%wu",
+ sr, &maxver, &path);
+ if (rc != 0)
+ return (NT_STATUS_INVALID_PARAMETER);
+ break;
+
+ case FSCTL_DFS_GET_REFERRALS_EX: /* XXX - todo */
+ default:
+ return (NT_STATUS_NOT_SUPPORTED);
+ }
+
+ reftype = smb_dfs_get_reftype((const char *)path);
+ switch (reftype) {
+ case DFS_REFERRAL_INVALID:
+ /* Need to check the error for this case */
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ case DFS_REFERRAL_DOMAIN:
+ case DFS_REFERRAL_DC:
+ /* MS-DFSC: this error is returned by non-DC root */
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ case DFS_REFERRAL_SYSVOL:
+ /* MS-DFSC: this error is returned by non-DC root */
+ return (NT_STATUS_NO_SUCH_DEVICE);
+
+ default:
+ break;
+ }
+
+ status = smb_dfs_referrals_get(sr, path, reftype, &refrsp);
+ if (status != NT_STATUS_SUCCESS)
+ return (status);
+
+ referrals = &refrsp.rp_referrals;
+ smb_dfs_encode_hdr(fsctl->out_mbc, referrals);
+
+ /*
+ * Server may respond with any referral version at or below
+ * the maximum specified in the request.
+ */
+ switch (maxver) {
+ case DFS_REFERRAL_V1:
+ status = smb_dfs_encode_refv1(sr, fsctl->out_mbc, referrals);
+ break;
+
+ case DFS_REFERRAL_V2:
+ status = smb_dfs_encode_refv2(sr, fsctl->out_mbc, referrals);
+ break;
+
+ case DFS_REFERRAL_V3:
+ status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals,
+ DFS_REFERRAL_V3);
+ break;
+
+ case DFS_REFERRAL_V4:
+ default:
+ status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals,
+ DFS_REFERRAL_V4);
+ break;
+ }
+
+ smb_dfs_referrals_free(&refrsp);
+
+ return (status);
+}
+
+/*
+ * [MS-DFSC]: REQ_GET_DFS_REFERRAL
+ *
+ * Determines the referral type based on the specified path:
+ *
+ * Domain referral:
+ * ""
+ *
+ * DC referral:
+ * \<domain>
+ *
+ * Sysvol referral:
+ * \<domain>\SYSVOL
+ * \<domain>\NETLOGON
+ *
+ * Root referral:
+ * \<domain>\<dfsname>
+ * \<server>\<dfsname>
+ *
+ * Link referral:
+ * \<domain>\<dfsname>\<linkpath>
+ * \<server>\<dfsname>\<linkpath>
+ */
+static dfs_reftype_t
+smb_dfs_get_reftype(const char *path)
+{
+ smb_unc_t unc;
+ dfs_reftype_t reftype = 0;
+
+ if (*path == '\0')
+ return (DFS_REFERRAL_DOMAIN);
+
+ if (smb_unc_init(path, &unc) != 0)
+ return (DFS_REFERRAL_INVALID);
+
+ if (unc.unc_path != NULL) {
+ reftype = DFS_REFERRAL_LINK;
+ } else if (unc.unc_share != NULL) {
+ if ((smb_strcasecmp(unc.unc_share, "SYSVOL", 0) == 0) ||
+ (smb_strcasecmp(unc.unc_share, "NETLOGON", 0) == 0)) {
+ reftype = DFS_REFERRAL_SYSVOL;
+ } else {
+ reftype = DFS_REFERRAL_ROOT;
+ }
+ } else if (unc.unc_server != NULL) {
+ reftype = DFS_REFERRAL_DC;
+ }
+
+ smb_unc_free(&unc);
+ return (reftype);
+}
+
+static void
+smb_dfs_encode_hdr(mbuf_chain_t *mbc, dfs_info_t *referrals)
+{
+ uint16_t path_consumed;
+ uint32_t flags;
+
+ path_consumed = smb_wcequiv_strlen(referrals->i_uncpath);
+ flags = DFS_HDRFLG_S;
+ if (referrals->i_type == DFS_OBJECT_ROOT)
+ flags |= DFS_HDRFLG_R;
+
+ /* Fill rep_param_mb in SMB1 caller. */
+ (void) smb_mbc_encodef(mbc, "wwl", path_consumed,
+ referrals->i_ntargets, flags);
+}
+
+static uint32_t
+smb_dfs_encode_refv1(smb_request_t *sr, mbuf_chain_t *mbc,
+ dfs_info_t *referrals)
+{
+ _NOTE(ARGUNUSED(sr))
+ uint16_t entsize, rep_bufsize;
+ uint16_t server_type;
+ uint16_t flags = 0;
+ uint16_t r;
+ char *target;
+
+ rep_bufsize = MBC_MAXBYTES(mbc);
+
+ server_type = (referrals->i_type == DFS_OBJECT_ROOT) ?
+ DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT;
+
+ target = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ for (r = 0; r < referrals->i_ntargets; r++) {
+ (void) snprintf(target, MAXPATHLEN, "\\%s\\%s",
+ referrals->i_targets[r].t_server,
+ referrals->i_targets[r].t_share);
+
+ entsize = DFS_REFV1_ENTSZ + smb_wcequiv_strlen(target) + 2;
+ if (entsize > rep_bufsize)
+ break;
+
+ (void) smb_mbc_encodef(mbc, "wwwwU",
+ DFS_REFERRAL_V1, entsize, server_type, flags, target);
+ rep_bufsize -= entsize;
+ }
+
+ kmem_free(target, MAXPATHLEN);
+
+ /*
+ * Need room for at least one entry.
+ * Windows will silently drop targets that do not fit in
+ * the response buffer.
+ */
+ if (r == 0) {
+ return (NT_STATUS_BUFFER_OVERFLOW);
+ }
+
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * Prepare a response with V2 referral format.
+ *
+ * Here is the response packet format.
+ * All the strings come after all the fixed size entry headers.
+ * These headers contain offsets to the strings at the end. Note
+ * that the two "dfs_path" after the last entry is shared between
+ * all the entries.
+ *
+ * ent1-hdr
+ * ent2-hdr
+ * ...
+ * entN-hdr
+ * dfs_path
+ * dfs_path
+ * target1
+ * target2
+ * ...
+ * targetN
+ *
+ * MS-DFSC mentions that strings can come after each entry header or all after
+ * the last entry header. Windows responses are in the format above.
+ */
+static uint32_t
+smb_dfs_encode_refv2(smb_request_t *sr, mbuf_chain_t *mbc,
+ dfs_info_t *referrals)
+{
+ _NOTE(ARGUNUSED(sr))
+ uint16_t entsize, rep_bufsize;
+ uint16_t server_type;
+ uint16_t flags = 0;
+ uint32_t proximity = 0;
+ uint16_t path_offs, altpath_offs, netpath_offs;
+ uint16_t targetsz, total_targetsz = 0;
+ uint16_t dfs_pathsz;
+ uint16_t r;
+
+ rep_bufsize = MBC_MAXBYTES(mbc);
+ dfs_pathsz = smb_wcequiv_strlen(referrals->i_uncpath) + 2;
+ entsize = DFS_REFV2_ENTSZ + dfs_pathsz + dfs_pathsz +
+ smb_dfs_referrals_unclen(referrals, 0);
+
+ if (entsize > rep_bufsize) {
+ /* need room for at least one referral */
+ return (NT_STATUS_BUFFER_OVERFLOW);
+ }
+
+ server_type = (referrals->i_type == DFS_OBJECT_ROOT) ?
+ DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT;
+
+ rep_bufsize -= entsize;
+ entsize = DFS_REFV2_ENTSZ;
+
+ for (r = 0; r < referrals->i_ntargets; r++) {
+ path_offs = (referrals->i_ntargets - r) * DFS_REFV2_ENTSZ;
+ altpath_offs = path_offs + dfs_pathsz;
+ netpath_offs = altpath_offs + dfs_pathsz + total_targetsz;
+ targetsz = smb_dfs_referrals_unclen(referrals, r);
+
+ if (r != 0) {
+ entsize = DFS_REFV2_ENTSZ + targetsz;
+ if (entsize > rep_bufsize)
+ /* silently drop targets that do not fit */
+ break;
+ rep_bufsize -= entsize;
+ }
+
+ (void) smb_mbc_encodef(mbc, "wwwwllwww",
+ DFS_REFERRAL_V2, DFS_REFV2_ENTSZ, server_type, flags,
+ proximity, referrals->i_timeout, path_offs, altpath_offs,
+ netpath_offs);
+
+ total_targetsz += targetsz;
+ }
+
+ smb_dfs_encode_targets(mbc, referrals);
+
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * Prepare a response with V3/V4 referral format.
+ *
+ * For more details, see comments for smb_dfs_encode_refv2() or see
+ * MS-DFSC specification.
+ */
+static uint32_t
+smb_dfs_encode_refv3x(smb_request_t *sr, mbuf_chain_t *mbc,
+ dfs_info_t *referrals,
+ uint16_t ver)
+{
+ _NOTE(ARGUNUSED(sr))
+ uint16_t entsize, rep_bufsize, hdrsize;
+ uint16_t server_type;
+ uint16_t flags = 0;
+ uint16_t path_offs, altpath_offs, netpath_offs;
+ uint16_t targetsz, total_targetsz = 0;
+ uint16_t dfs_pathsz;
+ uint16_t r;
+
+ hdrsize = (ver == DFS_REFERRAL_V3) ? DFS_REFV3_ENTSZ : DFS_REFV4_ENTSZ;
+ rep_bufsize = MBC_MAXBYTES(mbc);
+ dfs_pathsz = smb_wcequiv_strlen(referrals->i_uncpath) + 2;
+ entsize = hdrsize + dfs_pathsz + dfs_pathsz +
+ smb_dfs_referrals_unclen(referrals, 0);
+
+ if (entsize > rep_bufsize) {
+ /* need room for at least one referral */
+ return (NT_STATUS_BUFFER_OVERFLOW);
+ }
+
+ server_type = (referrals->i_type == DFS_OBJECT_ROOT) ?
+ DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT;
+
+ rep_bufsize -= entsize;
+
+ for (r = 0; r < referrals->i_ntargets; r++) {
+ path_offs = (referrals->i_ntargets - r) * hdrsize;
+ altpath_offs = path_offs + dfs_pathsz;
+ netpath_offs = altpath_offs + dfs_pathsz + total_targetsz;
+ targetsz = smb_dfs_referrals_unclen(referrals, r);
+
+ if (r != 0) {
+ entsize = hdrsize + targetsz;
+ if (entsize > rep_bufsize)
+ /* silently drop targets that do not fit */
+ break;
+ rep_bufsize -= entsize;
+ flags = 0;
+ } else if (ver == DFS_REFERRAL_V4) {
+ flags = DFS_ENTFLG_T;
+ }
+
+ (void) smb_mbc_encodef(mbc, "wwwwlwww16.",
+ ver, hdrsize, server_type, flags,
+ referrals->i_timeout, path_offs, altpath_offs,
+ netpath_offs);
+
+ total_targetsz += targetsz;
+ }
+
+ smb_dfs_encode_targets(mbc, referrals);
+
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * Encodes DFS path, and target strings which come after fixed header
+ * entries.
+ *
+ * Windows 2000 and earlier set the DFSAlternatePathOffset to point to
+ * an 8.3 string representation of the string pointed to by
+ * DFSPathOffset if it is not a legal 8.3 string. Otherwise, if
+ * DFSPathOffset points to a legal 8.3 string, DFSAlternatePathOffset
+ * points to a separate copy of the same string. Windows Server 2003,
+ * Windows Server 2008 and Windows Server 2008 R2 set the
+ * DFSPathOffset and DFSAlternatePathOffset fields to point to separate
+ * copies of the identical string.
+ *
+ * Following Windows 2003 and later here.
+ */
+static void
+smb_dfs_encode_targets(mbuf_chain_t *mbc, dfs_info_t *referrals)
+{
+ char *target;
+ int r;
+
+ (void) smb_mbc_encodef(mbc, "UU", referrals->i_uncpath,
+ referrals->i_uncpath);
+
+ target = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+ for (r = 0; r < referrals->i_ntargets; r++) {
+ (void) snprintf(target, MAXPATHLEN, "\\%s\\%s",
+ referrals->i_targets[r].t_server,
+ referrals->i_targets[r].t_share);
+ (void) smb_mbc_encodef(mbc, "U", target);
+ }
+ kmem_free(target, MAXPATHLEN);
+}
+
+/*
+ * Get referral information for the specified path from user space
+ * using a door call.
+ */
+static uint32_t
+smb_dfs_referrals_get(smb_request_t *sr, char *dfs_path, dfs_reftype_t reftype,
+ dfs_referral_response_t *refrsp)
+{
+ dfs_referral_query_t req;
+ int rc;
+
+ req.rq_type = reftype;
+ req.rq_path = dfs_path;
+
+ bzero(refrsp, sizeof (dfs_referral_response_t));
+ refrsp->rp_status = NT_STATUS_NOT_FOUND;
+
+ rc = smb_kdoor_upcall(sr->sr_server, SMB_DR_DFS_GET_REFERRALS,
+ &req, dfs_referral_query_xdr, refrsp, dfs_referral_response_xdr);
+
+ if (rc != 0 || refrsp->rp_status != ERROR_SUCCESS) {
+ return (NT_STATUS_NO_SUCH_DEVICE);
+ }
+
+ (void) strsubst(refrsp->rp_referrals.i_uncpath, '/', '\\');
+ return (NT_STATUS_SUCCESS);
+}
+
+static void
+smb_dfs_referrals_free(dfs_referral_response_t *refrsp)
+{
+ xdr_free(dfs_referral_response_xdr, (char *)refrsp);
+}
+
+/*
+ * Returns the Unicode string length for the target UNC of
+ * the specified entry by 'refno'
+ *
+ * Note that the UNC path should be encoded with ONE leading
+ * slash not two as is common to user-visible UNC paths.
+ */
+static uint16_t
+smb_dfs_referrals_unclen(dfs_info_t *referrals, uint16_t refno)
+{
+ uint16_t len;
+
+ if (refno >= referrals->i_ntargets)
+ return (0);
+
+ /* Encoded target UNC \server\share */
+ len = smb_wcequiv_strlen(referrals->i_targets[refno].t_server) +
+ smb_wcequiv_strlen(referrals->i_targets[refno].t_share) +
+ smb_wcequiv_strlen("\\\\") + 2; /* two '\' + NULL */
+
+ return (len);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_dispatch.c b/usr/src/uts/common/fs/smbsrv/smb_dispatch.c
index e9890c768a..5641ebfc81 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_dispatch.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_dispatch.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2007, 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.
*/
/*
@@ -143,6 +143,7 @@
static int is_andx_com(unsigned char);
static int smbsr_check_result(struct smb_request *, int, int);
+static void smb1_tq_work(void *);
static const smb_disp_entry_t const
smb_disp_table[SMB_COM_NUM] = {
@@ -304,7 +305,12 @@ smb_disp_table[SMB_COM_NUM] = {
{ "SmbTreeDisconnect", SMB_SDT_OPS(tree_disconnect), /* 0x71 113 */
0x71, PC_NETWORK_PROGRAM_1_0,
SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
- { "SmbNegotiate", SMB_SDT_OPS(negotiate), /* 0x72 114 */
+ /*
+ * NB: Negotiate gets special handling via
+ * smb_initial_request_handler. After that,
+ * another Negotiate is an invalid request.
+ */
+ { "SmbNegotiate", SMB_SDT_OPS(invalid), /* 0x72 114 */
0x72, PC_NETWORK_PROGRAM_1_0,
SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
{ "SmbSessionSetupX", SMB_SDT_OPS(session_setup_andx), /* 0x73 115 */
@@ -501,17 +507,102 @@ smbsr_cleanup(smb_request_t *sr)
sr->sr_state = SMB_REQ_STATE_CLEANED_UP;
mutex_exit(&sr->sr_mutex);
}
+
/*
- * smb_dispatch_request
+ * This is the SMB1 handler for new smb requests, called from
+ * smb_session_reader after SMB negotiate is done. For most SMB
+ * requests, we just enqueue them for the smb_session_worker to
+ * execute via the task queue, so they can block for resources
+ * without stopping the reader thread. A few protocol messages
+ * are special cases and are handled directly here in the reader
+ * thread so they don't wait for taskq scheduling. Later, a few
+ * MORE things could be handled here, such as REPLY messages
+ * (oplock break reply) and things like "NT_cancel".
*
- * Returns:
+ * This function must either enqueue the new request for
+ * execution via the task queue, or execute it directly
+ * and then free it. If this returns non-zero, the caller
+ * will drop the session.
+ */
+int
+smb1sr_newrq(smb_request_t *sr)
+{
+ uint32_t magic;
+
+ magic = SMB_READ_PROTOCOL(sr->sr_request_buf);
+ if (magic != SMB_PROTOCOL_MAGIC) {
+ smb_request_free(sr);
+ return (EPROTO);
+ }
+
+ if (sr->session->signing.flags & SMB_SIGNING_ENABLED) {
+ if (SMB_IS_NT_CANCEL(sr)) {
+ sr->session->signing.seqnum++;
+ sr->sr_seqnum = sr->session->signing.seqnum + 1;
+ sr->reply_seqnum = 0;
+ } else {
+ sr->session->signing.seqnum += 2;
+ sr->sr_seqnum = sr->session->signing.seqnum;
+ sr->reply_seqnum = sr->sr_seqnum + 1;
+ }
+ }
+
+ /*
+ * Submit the request to the task queue, which calls
+ * smb1_tq_work when the workload permits.
+ */
+ sr->sr_time_submitted = gethrtime();
+ sr->sr_state = SMB_REQ_STATE_SUBMITTED;
+ smb_srqueue_waitq_enter(sr->session->s_srqueue);
+ (void) taskq_dispatch(sr->sr_server->sv_worker_pool,
+ smb1_tq_work, sr, TQ_SLEEP);
+
+ return (0);
+}
+
+static void
+smb1_tq_work(void *arg)
+{
+ smb_request_t *sr;
+ smb_srqueue_t *srq;
+
+ sr = (smb_request_t *)arg;
+ SMB_REQ_VALID(sr);
+
+ srq = sr->session->s_srqueue;
+ smb_srqueue_waitq_to_runq(srq);
+ sr->sr_worker = curthread;
+ sr->sr_time_active = gethrtime();
+
+ mutex_enter(&sr->sr_mutex);
+ switch (sr->sr_state) {
+ case SMB_REQ_STATE_SUBMITTED:
+ mutex_exit(&sr->sr_mutex);
+ smb1sr_work(sr);
+ sr = NULL;
+ break;
+
+ default:
+ /*
+ * SMB1 requests that have been cancelled
+ * have no reply. Just free it.
+ */
+ sr->sr_state = SMB_REQ_STATE_COMPLETED;
+ mutex_exit(&sr->sr_mutex);
+ smb_request_free(sr);
+ break;
+ }
+ smb_srqueue_runq_exit(srq);
+}
+
+/*
+ * smb1sr_work
*
- * B_TRUE The caller must free the smb request passed in.
- * B_FALSE The caller must not access the smb request passed in. It has
- * been kept in an internal queue and may have already been freed.
+ * In most cases, this should free the request before return.
+ * Exceptions are when a dispatch function returns SDRC_SR_KEPT.
*/
-boolean_t
-smb_dispatch_request(struct smb_request *sr)
+void
+smb1sr_work(struct smb_request *sr)
{
smb_sdrc_t sdrc;
const smb_disp_entry_t *sdd;
@@ -521,6 +612,7 @@ smb_dispatch_request(struct smb_request *sr)
smb_server_t *server;
uint32_t byte_count;
uint32_t max_bytes;
+ uint16_t pid_hi, pid_lo;
session = sr->session;
server = session->s_server;
@@ -534,7 +626,9 @@ smb_dispatch_request(struct smb_request *sr)
sr->user_cr = zone_kcred();
sr->orig_request_hdr = sr->command.chain_offset;
- /* If this connection is shutting down just kill request */
+ /*
+ * Decode the SMB header.
+ */
if (smb_mbc_decodef(&sr->command, SMB_HEADER_ED_FMT,
&sr->smb_com,
&sr->smb_rcls,
@@ -542,15 +636,16 @@ smb_dispatch_request(struct smb_request *sr)
&sr->smb_err,
&sr->smb_flg,
&sr->smb_flg2,
- &sr->smb_pid_high,
+ &pid_hi,
sr->smb_sig,
&sr->smb_tid,
- &sr->smb_pid,
+ &pid_lo,
&sr->smb_uid,
&sr->smb_mid) != 0) {
disconnect = B_TRUE;
goto drop_connection;
}
+ sr->smb_pid = (pid_hi << 16) | pid_lo;
/*
* The reply "header" is filled in now even though it will,
@@ -575,10 +670,10 @@ smb_dispatch_request(struct smb_request *sr)
sr->smb_err,
sr->smb_flg,
sr->smb_flg2,
- sr->smb_pid_high,
+ pid_hi,
sr->smb_sig,
sr->smb_tid,
- sr->smb_pid,
+ pid_lo,
sr->smb_uid,
sr->smb_mid);
sr->first_smb_com = sr->smb_com;
@@ -596,7 +691,7 @@ smb_dispatch_request(struct smb_request *sr)
andx_more:
sdd = &smb_disp_table[sr->smb_com];
ASSERT(sdd->sdt_function);
- sds = &server->sv_disp_stats[sr->smb_com];
+ sds = &server->sv_disp_stats1[sr->smb_com];
if (smb_mbc_decodef(&sr->command, "b", &sr->smb_wct) != 0) {
disconnect = B_TRUE;
@@ -695,20 +790,6 @@ andx_more:
}
}
- /*
- * If the command is not a read raw request we can set the
- * state of the session back to SMB_SESSION_STATE_NEGOTIATED
- * (if the current state is SMB_SESSION_STATE_OPLOCK_BREAKING).
- * Otherwise we let the read raw handler to deal with it.
- */
- smb_rwx_rwenter(&session->s_lock, RW_READER);
- if (session->s_state == SMB_SESSION_STATE_OPLOCK_BREAKING) {
- (void) smb_rwx_rwupgrade(&session->s_lock);
- if (session->s_state == SMB_SESSION_STATE_OPLOCK_BREAKING)
- session->s_state = SMB_SESSION_STATE_NEGOTIATED;
- }
- smb_rwx_rwexit(&session->s_lock);
-
sr->sr_time_start = gethrtime();
if ((sdrc = (*sdd->sdt_pre_op)(sr)) == SDRC_SUCCESS)
sdrc = (*sdd->sdt_function)(sr);
@@ -731,10 +812,13 @@ andx_more:
goto drop_connection;
case SDRC_NO_REPLY:
- return (B_TRUE);
+ /* will free sr */
+ goto out;
case SDRC_SR_KEPT:
- return (B_FALSE);
+ /* Do NOT free sr */
+ sr = NULL;
+ goto out;
case SDRC_ERROR:
goto report_error;
@@ -791,7 +875,13 @@ drop_connection:
smb_rwx_rwexit(&session->s_lock);
}
- return (B_TRUE);
+out:
+ if (sr != NULL) {
+ mutex_enter(&sr->sr_mutex);
+ sr->sr_state = SMB_REQ_STATE_COMPLETED;
+ mutex_exit(&sr->sr_mutex);
+ smb_request_free(sr);
+ }
}
int
@@ -801,7 +891,8 @@ smbsr_encode_empty_result(struct smb_request *sr)
}
int
-smbsr_encode_result(struct smb_request *sr, int wct, int bcc, char *fmt, ...)
+smbsr_encode_result(struct smb_request *sr, int wct, int bcc,
+ const char *fmt, ...)
{
va_list ap;
@@ -877,7 +968,7 @@ smbsr_check_result(struct smb_request *sr, int wct, int bcc)
}
int
-smbsr_decode_vwv(struct smb_request *sr, char *fmt, ...)
+smbsr_decode_vwv(struct smb_request *sr, const char *fmt, ...)
{
int rc;
va_list ap;
@@ -892,7 +983,7 @@ smbsr_decode_vwv(struct smb_request *sr, char *fmt, ...)
}
int
-smbsr_decode_data(struct smb_request *sr, char *fmt, ...)
+smbsr_decode_data(struct smb_request *sr, const char *fmt, ...)
{
int rc;
va_list ap;
@@ -915,10 +1006,14 @@ smbsr_decode_data_avail(smb_request_t *sr)
void
smbsr_send_reply(smb_request_t *sr)
{
+ uint16_t pid_hi, pid_lo;
+
if (SMB_TREE_IS_CASEINSENSITIVE(sr))
sr->smb_flg |= SMB_FLAGS_CASE_INSENSITIVE;
else
sr->smb_flg &= ~SMB_FLAGS_CASE_INSENSITIVE;
+ pid_hi = sr->smb_pid >> 16;
+ pid_lo = (uint16_t)sr->smb_pid;
(void) smb_mbc_poke(&sr->reply, 0, SMB_HEADER_ED_FMT,
sr->first_smb_com,
@@ -927,83 +1022,32 @@ smbsr_send_reply(smb_request_t *sr)
sr->smb_err,
sr->smb_flg | SMB_FLAGS_REPLY,
sr->smb_flg2,
- sr->smb_pid_high,
+ pid_hi,
sr->smb_sig,
sr->smb_tid,
- sr->smb_pid,
+ pid_lo,
sr->smb_uid,
sr->smb_mid);
if (sr->session->signing.flags & SMB_SIGNING_ENABLED)
smb_sign_reply(sr, NULL);
- smb_server_inc_req(sr->session->s_server);
if (smb_session_send(sr->session, 0, &sr->reply) == 0)
sr->reply.chain = 0;
}
-/*
- * Map errno values to SMB and NT status values.
- * Note: ESRCH is a special case to handle a streams lookup failure.
- */
-static const struct {
- int errnum;
- int errcls;
- int errcode;
- DWORD status32;
-} const smb_errno_map[] = {
- { ENOSPC, ERRDOS, ERROR_DISK_FULL, NT_STATUS_DISK_FULL },
- { EDQUOT, ERRDOS, ERROR_DISK_FULL, NT_STATUS_DISK_FULL },
- { EPERM, ERRSRV, ERRaccess, NT_STATUS_ACCESS_DENIED },
- { ENOTDIR, ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND },
- { EISDIR, ERRDOS, ERRbadpath, NT_STATUS_FILE_IS_A_DIRECTORY },
- { ENOENT, ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE },
- { ENOTEMPTY, ERRDOS, ERROR_DIR_NOT_EMPTY,
- NT_STATUS_DIRECTORY_NOT_EMPTY },
- { EILSEQ, ERRDOS, ERROR_INVALID_NAME,
- NT_STATUS_OBJECT_NAME_INVALID },
- { EACCES, ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED },
- { ENOMEM, ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY },
- { EIO, ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR },
- { EXDEV, ERRSRV, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE },
- { EREMOTE, ERRSRV, ERRbadpath, NT_STATUS_PATH_NOT_COVERED},
- { ENAMETOOLONG, ERRDOS, ERROR_INVALID_NAME,
- NT_STATUS_OBJECT_NAME_INVALID },
- { EROFS, ERRHRD, ERRnowrite, NT_STATUS_ACCESS_DENIED },
- { ESTALE, ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE },
- { EBADF, ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE },
- { ENOTSOCK, ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE },
- { EPIPE, ERRDOS, ERROR_BROKEN_PIPE, NT_STATUS_PIPE_BROKEN },
- { EEXIST, ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION },
- { ENXIO, ERRSRV, ERRinvdevice, NT_STATUS_BAD_DEVICE_TYPE },
- { ESRCH, ERRDOS, ERROR_FILE_NOT_FOUND,
- NT_STATUS_OBJECT_NAME_NOT_FOUND },
- /*
- * It's not clear why smb_read_common effectively returns
- * ERRnoaccess if a range lock prevents access and smb_write_common
- * effectively returns ERRaccess. This table entry is used by
- * smb_read_common and preserves the behavior that was there before.
- */
- { ERANGE, ERRDOS, ERRnoaccess, NT_STATUS_FILE_LOCK_CONFLICT }
-};
-
void
smbsr_map_errno(int errnum, smb_error_t *err)
{
- int i;
-
- for (i = 0; i < sizeof (smb_errno_map)/sizeof (smb_errno_map[0]); ++i) {
- if (smb_errno_map[i].errnum == errnum) {
- err->status = smb_errno_map[i].status32;
- err->errcls = smb_errno_map[i].errcls;
- err->errcode = smb_errno_map[i].errcode;
- return;
- }
- }
+ uint32_t status;
+ uint16_t doserr;
+
+ status = smb_errno2status(errnum);
+ doserr = smb_status2doserr(status);
- err->status = NT_STATUS_INTERNAL_ERROR;
- err->errcls = ERRDOS;
- err->errcode = ERROR_INTERNAL_ERROR;
+ err->status = status;
+ err->errcls = ERRDOS;
+ err->errcode = doserr;
}
void
@@ -1019,7 +1063,26 @@ smbsr_errno(struct smb_request *sr, int errnum)
void
smbsr_status(smb_request_t *sr, DWORD status, uint16_t errcls, uint16_t errcode)
{
+
sr->smb_error.status = status;
+
+ /*
+ * This function is SMB1 specific. While adding SMB2 support,
+ * calls to this function have been removed from common code.
+ * In case we missed any, check for SMB2 callers here, and
+ * vector into a wrapper function that we can watch for...
+ * (and track down with dtrace:)
+ */
+ if (sr->session->dialect >= SMB_VERS_2_BASE) {
+ smbsr_status_smb2(sr, status);
+ return;
+ }
+
+ if (status != 0 && errcls == 0 && errcode == 0) {
+ errcls = ERRDOS;
+ errcode = smb_status2doserr(status);
+ }
+
sr->smb_error.errcls = errcls;
sr->smb_error.errcode = errcode;
@@ -1114,10 +1177,6 @@ is_andx_com(unsigned char com)
/*
* Invalid command stubs.
*
- * SmbWriteComplete is sent to acknowledge completion of raw write requests.
- * We never send raw write commands to other servers so, if we receive
- * SmbWriteComplete, we treat it as an error.
- *
* The Read/Write Block Multiplexed (mpx) protocol is used to maximize
* performance when reading/writing a large block of data: it can be
* used in parallel with other client/server operations. The mpx sub-
@@ -1166,23 +1225,17 @@ smb_com_invalid(smb_request_t *sr)
void
smb_dispatch_stats_init(smb_server_t *sv)
{
- smb_disp_stats_t *sds = sv->sv_disp_stats;
+ smb_disp_stats_t *sds = sv->sv_disp_stats1;
smb_kstat_req_t *ksr;
- int ks_ndata;
int i;
- ksr = ((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_reqs;
+ ksr = ((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_reqs1;
for (i = 0; i < SMB_COM_NUM; i++, ksr++) {
smb_latency_init(&sds[i].sdt_lat);
(void) strlcpy(ksr->kr_name, smb_disp_table[i].sdt_name,
sizeof (ksr->kr_name));
}
- /* Legacy Statistics */
- for (i = 0, ks_ndata = 0; i < SMB_COM_NUM; i++) {
- if (smb_disp_table[i].sdt_function != smb_com_invalid)
- ks_ndata++;
- }
}
/*
@@ -1193,7 +1246,7 @@ smb_dispatch_stats_init(smb_server_t *sv)
void
smb_dispatch_stats_fini(smb_server_t *sv)
{
- smb_disp_stats_t *sds = sv->sv_disp_stats;
+ smb_disp_stats_t *sds = sv->sv_disp_stats1;
int i;
for (i = 0; i < SMB_COM_NUM; i++)
@@ -1204,7 +1257,7 @@ void
smb_dispatch_stats_update(smb_server_t *sv,
smb_kstat_req_t *ksr, int first, int nreq)
{
- smb_disp_stats_t *sds = sv->sv_disp_stats;
+ smb_disp_stats_t *sds = sv->sv_disp_stats1;
int i;
int last;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_echo.c b/usr/src/uts/common/fs/smbsrv/smb_echo.c
index 4cc3925b16..d9ffeb38de 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_echo.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_echo.c
@@ -21,6 +21,8 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
@@ -57,6 +59,10 @@ smb_com_echo(struct smb_request *sr)
unsigned short i;
struct mbuf_chain reply;
char *data;
+ uint16_t pid_hi, pid_lo;
+
+ pid_hi = sr->smb_pid >> 16;
+ pid_lo = (uint16_t)sr->smb_pid;
if (smbsr_decode_vwv(sr, "w", &necho) != 0)
return (SDRC_ERROR);
@@ -92,10 +98,10 @@ smb_com_echo(struct smb_request *sr)
sr->smb_err,
sr->smb_flg | SMB_FLAGS_REPLY,
sr->smb_flg2,
- sr->smb_pid_high,
+ pid_hi,
sr->smb_sig,
sr->smb_tid,
- sr->smb_pid,
+ pid_lo,
sr->smb_uid,
sr->smb_mid);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_errno.c b/usr/src/uts/common/fs/smbsrv/smb_errno.c
new file mode 100644
index 0000000000..533d099b4a
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb_errno.c
@@ -0,0 +1,126 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Translate Unix errno values to NT status, and NT status to
+ * DOS-style error class+code (for SMB1)
+ */
+
+#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb_kstat.h>
+
+#include "smbclnt/smb_status2winerr.h"
+
+
+/*
+ * Map Unix errno values to NT status values.
+ */
+
+struct errno2status {
+ int errnum;
+ uint_t status;
+};
+
+static const struct errno2status
+smb_errno2status_map[] = {
+ { EPERM, NT_STATUS_ACCESS_DENIED },
+ { ENOENT, NT_STATUS_NO_SUCH_FILE },
+ /* NB: ESRCH is used to represent stream lookup failures. */
+ { ESRCH, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { EINTR, NT_STATUS_CANCELLED },
+ { EIO, NT_STATUS_IO_DEVICE_ERROR },
+ { ENXIO, NT_STATUS_BAD_DEVICE_TYPE },
+ /* E2BIG, ENOEXEC */
+ { EBADF, NT_STATUS_INVALID_HANDLE },
+ /* ECHILD, EAGAIN */
+ { ENOMEM, NT_STATUS_NO_MEMORY },
+ { EACCES, NT_STATUS_ACCESS_DENIED },
+ /* EFAULT, ENOTBLK, EBUSY */
+ { EEXIST, NT_STATUS_OBJECT_NAME_COLLISION },
+ { EXDEV, NT_STATUS_NOT_SAME_DEVICE },
+ { ENODEV, NT_STATUS_NO_SUCH_DEVICE },
+ /* ENOTDIR should be: NT_STATUS_NOT_A_DIRECTORY, but not yet */
+ { ENOTDIR, NT_STATUS_OBJECT_PATH_NOT_FOUND },
+ { EISDIR, NT_STATUS_FILE_IS_A_DIRECTORY },
+ { EINVAL, NT_STATUS_INVALID_PARAMETER },
+ { ENFILE, NT_STATUS_TOO_MANY_OPENED_FILES },
+ { EMFILE, NT_STATUS_TOO_MANY_OPENED_FILES },
+ { ENOTTY, NT_STATUS_INVALID_DEVICE_REQUEST },
+ /* ENOTTY, ETXTBSY, EFBIG */
+ { ENOSPC, NT_STATUS_DISK_FULL },
+ /* ESPIPE */
+ { EROFS, NT_STATUS_ACCESS_DENIED },
+ { EMLINK, NT_STATUS_TOO_MANY_LINKS },
+ { EPIPE, NT_STATUS_PIPE_BROKEN },
+ /* EDOM */
+ /* NB: ERANGE is used to represent lock range I/O conflicts. */
+ { ERANGE, NT_STATUS_FILE_LOCK_CONFLICT },
+ /* ENOMSG, EIDRM, ... */
+ { ENOTSUP, NT_STATUS_NOT_SUPPORTED },
+ { EDQUOT, NT_STATUS_DISK_FULL },
+ { EREMOTE, NT_STATUS_PATH_NOT_COVERED},
+ { ENAMETOOLONG, NT_STATUS_OBJECT_NAME_INVALID },
+ { EILSEQ, NT_STATUS_OBJECT_NAME_INVALID },
+ { ENOTEMPTY, NT_STATUS_DIRECTORY_NOT_EMPTY },
+ { ENOTSOCK, NT_STATUS_INVALID_HANDLE },
+ { ESTALE, NT_STATUS_INVALID_HANDLE },
+ { 0, 0 }
+};
+
+uint_t
+smb_errno2status(int errnum)
+{
+ const struct errno2status *es;
+
+ if (errnum == 0)
+ return (0);
+
+ for (es = smb_errno2status_map; es->errnum != 0; es++)
+ if (es->errnum == errnum)
+ return (es->status);
+
+ return (NT_STATUS_INTERNAL_ERROR);
+}
+
+/*
+ * Map NT Status codes to Win32 API error numbers.
+ * But note: we only want the ones below 0xFFFF,
+ * which can be returned in SMB with class=DOSERR.
+ */
+uint16_t
+smb_status2doserr(uint_t status)
+{
+ const struct status2winerr *sw;
+
+ if (status == 0)
+ return (0);
+
+ for (sw = smb_status2winerr_map; sw->status != 0; sw++)
+ if (sw->status == status && (sw->winerr < 0xFFFF))
+ return ((uint16_t)sw->winerr);
+
+ return (ERROR_GEN_FAILURE);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_find.c b/usr/src/uts/common/fs/smbsrv/smb_find.c
index eecbeff4df..23b9a5dc61 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_find.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_find.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
@@ -231,6 +231,7 @@ smb_com_search(smb_request_t *sr)
smb_odir_t *od;
smb_fileinfo_t fileinfo;
smb_odir_resume_t odir_resume;
+ uint32_t status;
uint16_t eos;
to_upper = B_FALSE;
@@ -292,9 +293,9 @@ smb_com_search(smb_request_t *sr)
client_key = 0;
if (find_first) {
- odid = smb_odir_open(sr, pn->pn_path, sattr, 0);
- if (odid == 0) {
- if (sr->smb_error.status == NT_STATUS_ACCESS_DENIED)
+ status = smb_odir_openpath(sr, pn->pn_path, sattr, 0, &od);
+ if (status != 0) {
+ if (status == NT_STATUS_ACCESS_DENIED)
smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
ERRDOS, ERROR_NO_MORE_FILES);
return (SDRC_ERROR);
@@ -304,9 +305,9 @@ smb_com_search(smb_request_t *sr)
&resume_char, &index, &odid, &client_key) != 0) {
return (SDRC_ERROR);
}
+ od = smb_tree_lookup_odir(sr, odid);
}
- od = smb_tree_lookup_odir(sr, odid);
if (od == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
ERRDOS, ERROR_INVALID_HANDLE);
@@ -314,9 +315,13 @@ smb_com_search(smb_request_t *sr)
}
if (!find_first) {
- odir_resume.or_type = SMB_ODIR_RESUME_IDX;
- odir_resume.or_idx = index;
- smb_odir_resume_at(od, &odir_resume);
+ if ((od->d_flags & SMB_ODIR_FLAG_WILDCARDS) == 0) {
+ od->d_eof = B_TRUE;
+ } else {
+ odir_resume.or_type = SMB_ODIR_RESUME_IDX;
+ odir_resume.or_idx = index;
+ smb_odir_resume_at(od, &odir_resume);
+ }
}
(void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
@@ -354,6 +359,8 @@ smb_com_search(smb_request_t *sr)
count++;
index++;
}
+ if (eos && rc == ENOENT)
+ rc = 0;
if (rc != 0) {
smb_odir_close(od);
@@ -408,6 +415,7 @@ smb_com_find(smb_request_t *sr)
char name83[SMB_SHORTNAMELEN];
smb_odir_t *od;
smb_fileinfo_t fileinfo;
+ uint32_t status;
uint16_t eos;
smb_pathname_t *pn;
@@ -442,17 +450,19 @@ smb_com_find(smb_request_t *sr)
client_key = 0;
if (find_first) {
- odid = smb_odir_open(sr, pn->pn_path, sattr, 0);
- if (odid == 0)
+ status = smb_odir_openpath(sr, pn->pn_path, sattr, 0, &od);
+ if (status != 0) {
+ smbsr_error(sr, status, 0, 0);
return (SDRC_ERROR);
+ }
} else {
if (smb_mbc_decodef(&sr->smb_data, "b12.wwl",
&resume_char, &index, &odid, &client_key) != 0) {
return (SDRC_ERROR);
}
+ od = smb_tree_lookup_odir(sr, odid);
}
- od = smb_tree_lookup_odir(sr, odid);
if (od == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
ERRDOS, ERROR_INVALID_HANDLE);
@@ -460,9 +470,13 @@ smb_com_find(smb_request_t *sr)
}
if (!find_first) {
- odir_resume.or_type = SMB_ODIR_RESUME_IDX;
- odir_resume.or_idx = index;
- smb_odir_resume_at(od, &odir_resume);
+ if ((od->d_flags & SMB_ODIR_FLAG_WILDCARDS) == 0) {
+ od->d_eof = B_TRUE;
+ } else {
+ odir_resume.or_type = SMB_ODIR_RESUME_IDX;
+ odir_resume.or_idx = index;
+ smb_odir_resume_at(od, &odir_resume);
+ }
}
(void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
@@ -498,6 +512,8 @@ smb_com_find(smb_request_t *sr)
count++;
index++;
}
+ if (eos && rc == ENOENT)
+ rc = 0;
if (rc != 0) {
smb_odir_close(od);
@@ -612,13 +628,14 @@ smb_com_find_unique(struct smb_request *sr)
{
int rc;
uint16_t count, maxcount, index;
- uint16_t sattr, odid;
+ uint16_t sattr;
smb_pathname_t *pn;
unsigned char resume_char = '\0';
uint32_t client_key = 0;
char name83[SMB_SHORTNAMELEN];
smb_odir_t *od;
smb_fileinfo_t fileinfo;
+ uint32_t status;
uint16_t eos;
smb_vdb_t *vdb;
@@ -646,10 +663,11 @@ smb_com_find_unique(struct smb_request *sr)
(void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
- odid = smb_odir_open(sr, pn->pn_path, sattr, 0);
- if (odid == 0)
+ status = smb_odir_openpath(sr, pn->pn_path, sattr, 0, &od);
+ if (status != 0) {
+ smbsr_error(sr, status, 0, 0);
return (SDRC_ERROR);
- od = smb_tree_lookup_odir(sr, odid);
+ }
if (od == NULL)
return (SDRC_ERROR);
@@ -673,8 +691,8 @@ smb_com_find_unique(struct smb_request *sr)
smb_name83(fileinfo.fi_shortname, name83, SMB_SHORTNAMELEN);
(void) smb_mbc_encodef(&sr->reply, "b11c.wwlbYl13c",
- resume_char, name83, index, odid, client_key,
- fileinfo.fi_dosattr & 0xff,
+ resume_char, name83, index, od->d_odid,
+ client_key, fileinfo.fi_dosattr & 0xff,
smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec),
(int32_t)fileinfo.fi_size,
fileinfo.fi_shortname);
@@ -682,6 +700,8 @@ smb_com_find_unique(struct smb_request *sr)
count++;
index++;
}
+ if (eos && rc == ENOENT)
+ rc = 0;
smb_odir_close(od);
smb_odir_release(od);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_fsinfo.c b/usr/src/uts/common/fs/smbsrv/smb_fsinfo.c
index 519bc1ff1f..e83a8a79c1 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_fsinfo.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_fsinfo.c
@@ -20,44 +20,13 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smbinfo.h>
-/*
- * smb_fssize_t
- * volume_units and volume avail are the total allocated and
- * available units on the volume.
- * caller_units and caller_avail are the allocated and available
- * units on the volume for the user associated with the calling
- * thread.
- */
-typedef struct smb_fssize {
- uint64_t fs_volume_units;
- uint64_t fs_volume_avail;
- uint64_t fs_caller_units;
- uint64_t fs_caller_avail;
- uint32_t fs_sectors_per_unit;
- uint32_t fs_bytes_per_sector;
-} smb_fssize_t;
-
-/*
- * File System Control Flags for smb_com_trans2_query|set_fs_information
- * level SMB_FILE_FS_CONTROL_INFORMATION
- */
-#define FILE_VC_QUOTA_TRACK 0x00000001
-#define FILE_VC_QUOTA_ENFORCE 0x00000002
-#define FILE_VC_CONTENT_INDEX_DISABLED 0x00000008
-#define FILE_VC_LOG_QUOTA_THRESHOLD 0x00000010
-#define FILE_VC_LOG_QUOTA_LIMIT 0x00000020
-#define FILE_VC_LOG_VOLUME_THRESHOLD 0x00000040
-#define FILE_VC_LOG_VOLUME_LIMIT 0x00000080
-#define FILE_VC_QUOTAS_INCOMPLETE 0x00000100
-#define FILE_VC_QUOTAS_REBUILDING 0x00000200
-
-static int smb_fssize(smb_request_t *, smb_fssize_t *);
static int smb_trans2_set_fs_ctrl_info(smb_request_t *, smb_xa_t *);
/*
@@ -409,36 +378,39 @@ smb_com_trans2_query_fs_information(smb_request_t *sr, smb_xa_t *xa)
* is specfied as unlimited. A quota limit of 0 means there is no
* quota specified for the user.
*
- * Returns: 0 - success
- * -1 - error. Error status set in sr.
+ * Returns: 0 (success) or an errno value
*/
-static int
+int
smb_fssize(smb_request_t *sr, smb_fssize_t *fssize)
{
smb_node_t *node;
struct statvfs64 df;
uid_t uid;
smb_quota_t quota;
- int rc, bytes_per_unit;
+ int spu; /* sectors per unit */
+ int rc;
bzero(fssize, sizeof (smb_fssize_t));
node = sr->tid_tree->t_snode;
- if ((rc = smb_fsop_statfs(sr->user_cr, node, &df)) != 0) {
- smbsr_errno(sr, rc);
- return (-1);
- }
+ if ((rc = smb_fsop_statfs(sr->user_cr, node, &df)) != 0)
+ return (rc);
+
+ if (df.f_frsize < DEV_BSIZE)
+ df.f_frsize = DEV_BSIZE;
+ if (df.f_bsize < df.f_frsize)
+ df.f_bsize = df.f_frsize;
+ spu = df.f_bsize / df.f_frsize;
+
+ fssize->fs_bytes_per_sector = (uint16_t)df.f_frsize;
+ fssize->fs_sectors_per_unit = spu;
- fssize->fs_bytes_per_sector = 512;
- fssize->fs_sectors_per_unit = df.f_frsize >> 9;
if (df.f_bavail > df.f_blocks)
df.f_bavail = 0;
- fssize->fs_volume_units = df.f_blocks;
- fssize->fs_volume_avail = df.f_bavail;
- fssize->fs_caller_units = df.f_blocks;
- fssize->fs_caller_avail = df.f_bavail;
- bytes_per_unit =
- fssize->fs_bytes_per_sector * fssize->fs_sectors_per_unit;
+ fssize->fs_volume_units = df.f_blocks / spu;
+ fssize->fs_volume_avail = df.f_bavail / spu;
+ fssize->fs_caller_units = df.f_blocks / spu;
+ fssize->fs_caller_avail = df.f_bavail / spu;
if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA))
return (0);
@@ -448,12 +420,12 @@ smb_fssize(smb_request_t *sr, smb_fssize_t *fssize)
return (0);
if ((quota.q_limit != SMB_QUOTA_UNLIMITED) && (quota.q_limit != 0)) {
- fssize->fs_caller_units = quota.q_limit / bytes_per_unit;
+ fssize->fs_caller_units = quota.q_limit / df.f_bsize;
if (quota.q_limit <= quota.q_used)
fssize->fs_caller_avail = 0;
else
fssize->fs_caller_avail =
- (quota.q_limit - quota.q_used) / bytes_per_unit;
+ (quota.q_limit - quota.q_used) / df.f_bsize;
}
return (0);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_fsops.c b/usr/src/uts/common/fs/smbsrv/smb_fsops.c
index c1d96faa43..9b860bb4c1 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_fsops.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_fsops.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 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/sid.h>
@@ -785,13 +785,13 @@ smb_fsop_remove(
*
* It is assumed that fnode is not a link.
*/
-int
+uint32_t
smb_fsop_remove_streams(smb_request_t *sr, cred_t *cr, smb_node_t *fnode)
{
int rc, flags = 0;
- uint16_t odid;
smb_odir_t *od;
smb_odirent_t *odirent;
+ uint32_t status;
boolean_t eos;
ASSERT(sr);
@@ -800,15 +800,11 @@ smb_fsop_remove_streams(smb_request_t *sr, cred_t *cr, smb_node_t *fnode)
ASSERT(fnode->n_magic == SMB_NODE_MAGIC);
ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING);
- if (SMB_TREE_CONTAINS_NODE(sr, fnode) == 0) {
- smbsr_errno(sr, EACCES);
- return (-1);
- }
+ if (SMB_TREE_CONTAINS_NODE(sr, fnode) == 0)
+ return (NT_STATUS_ACCESS_DENIED);
- if (SMB_TREE_IS_READONLY(sr)) {
- smbsr_errno(sr, EROFS);
- return (-1);
- }
+ if (SMB_TREE_IS_READONLY(sr))
+ return (NT_STATUS_ACCESS_DENIED);
if (SMB_TREE_IS_CASEINSENSITIVE(sr))
flags = SMB_IGNORE_CASE;
@@ -816,14 +812,16 @@ smb_fsop_remove_streams(smb_request_t *sr, cred_t *cr, smb_node_t *fnode)
if (SMB_TREE_SUPPORTS_CATIA(sr))
flags |= SMB_CATIA;
- if ((odid = smb_odir_openat(sr, fnode)) == 0) {
- smbsr_errno(sr, ENOENT);
- return (-1);
- }
-
- if ((od = smb_tree_lookup_odir(sr, odid)) == NULL) {
- smbsr_errno(sr, ENOENT);
- return (-1);
+ status = smb_odir_openat(sr, fnode, &od);
+ switch (status) {
+ case 0:
+ break;
+ case NT_STATUS_NO_SUCH_FILE:
+ case NT_STATUS_NOT_SUPPORTED:
+ /* No streams to remove. */
+ return (0);
+ default:
+ return (status);
}
odirent = kmem_alloc(sizeof (smb_odirent_t), KM_SLEEP);
@@ -835,10 +833,14 @@ smb_fsop_remove_streams(smb_request_t *sr, cred_t *cr, smb_node_t *fnode)
flags, cr);
}
kmem_free(odirent, sizeof (smb_odirent_t));
+ if (eos && rc == ENOENT)
+ rc = 0;
smb_odir_close(od);
smb_odir_release(od);
- return (rc);
+ if (rc)
+ status = smb_errno2status(rc);
+ return (status);
}
/*
@@ -1310,6 +1312,78 @@ smb_fsop_setattr(
}
/*
+ * Support for SMB2 setinfo FileValidDataLengthInformation.
+ * Free data from the specified offset to EoF.
+ *
+ * This can effectively truncate data. It truncates the data
+ * leaving the file size as it was, leaving zeros after the
+ * offset specified here. That is effectively modifying the
+ * file content, so for access control this is a write.
+ */
+int
+smb_fsop_set_data_length(
+ smb_request_t *sr,
+ cred_t *cr,
+ smb_node_t *node,
+ offset_t end_of_data)
+{
+ flock64_t flk;
+ uint32_t status;
+ uint32_t access = FILE_WRITE_DATA;
+ int rc;
+
+ ASSERT(cr);
+ ASSERT(node);
+ ASSERT(node->n_magic == SMB_NODE_MAGIC);
+ ASSERT(node->n_state != SMB_NODE_STATE_DESTROYING);
+
+ if (SMB_TREE_CONTAINS_NODE(sr, node) == 0)
+ return (EACCES);
+
+ if (SMB_TREE_IS_READONLY(sr))
+ return (EROFS);
+
+ if (SMB_TREE_HAS_ACCESS(sr, access) == 0)
+ return (EACCES);
+
+ /*
+ * The file system cannot detect pending READDONLY
+ * (i.e. if the file has been opened readonly but
+ * not yet closed) so we need to test READONLY here.
+ *
+ * Note that file handle that were opened before the
+ * READONLY flag was set in the node (or the FS) are
+ * immune to that change, and remain writable.
+ */
+ if (sr->fid_ofile) {
+ if (SMB_OFILE_IS_READONLY(sr->fid_ofile))
+ return (EACCES);
+ } else {
+ /* This requires an open file. */
+ return (EACCES);
+ }
+
+ /*
+ * SMB checks access on open and retains an access granted
+ * mask for use while the file is open. ACL changes should
+ * not affect access to an open file.
+ *
+ * If the setattr is being performed on an ofile:
+ * - Check the ofile's access granted mask to see if this
+ * modification should be permitted (FILE_WRITE_DATA)
+ */
+ status = smb_ofile_access(sr->fid_ofile, cr, access);
+ if (status != NT_STATUS_SUCCESS)
+ return (EACCES);
+
+ bzero(&flk, sizeof (flk));
+ flk.l_start = end_of_data;
+
+ rc = smb_vop_space(node->vp, F_FREESP, &flk, FWRITE, 0LL, cr);
+ return (rc);
+}
+
+/*
* smb_fsop_read
*
* All SMB functions should use this wrapper to ensure that
@@ -1532,10 +1606,6 @@ smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
ASSERT(snode->n_magic == SMB_NODE_MAGIC);
ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
- /* Requests for no access should be denied. */
- if (faccess == 0)
- return (NT_STATUS_ACCESS_DENIED);
-
if (SMB_TREE_IS_READONLY(sr)) {
if (faccess & (FILE_WRITE_DATA|FILE_APPEND_DATA|
FILE_WRITE_EA|FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES|
diff --git a/usr/src/uts/common/fs/smbsrv/smb_init.c b/usr/src/uts/common/fs/smbsrv/smb_init.c
index b2028603dc..2843537ada 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_init.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_init.c
@@ -183,7 +183,7 @@ _init(void)
}
if ((rc = mod_install(&modlinkage)) != 0) {
- (void) smb_server_g_fini();
+ smb_server_g_fini();
}
return (rc);
@@ -200,8 +200,11 @@ _fini(void)
{
int rc;
+ if (smb_server_get_count() != 0)
+ return (EBUSY);
+
if ((rc = mod_remove(&modlinkage)) == 0) {
- rc = smb_server_g_fini();
+ smb_server_g_fini();
}
return (rc);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_kshare.c b/usr/src/uts/common/fs/smbsrv/smb_kshare.c
index 53e900d0ed..bf3129d130 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_kshare.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_kshare.c
@@ -323,7 +323,7 @@ smb_kshare_init(smb_server_t *sv)
int
smb_kshare_start(smb_server_t *sv)
{
- smb_thread_init(&sv->sv_export.e_unexport_thread, "smb_thread_unexport",
+ smb_thread_init(&sv->sv_export.e_unexport_thread, "smb_kshare_unexport",
smb_kshare_unexport_thread, sv, smbsrv_base_pri);
return (smb_thread_start(&sv->sv_export.e_unexport_thread));
diff --git a/usr/src/uts/common/fs/smbsrv/smb_kutil.c b/usr/src/uts/common/fs/smbsrv/smb_kutil.c
index 2f0d327fe8..e5ac75cbbc 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 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/param.h>
@@ -73,7 +73,8 @@ static const int days_in_month[] = {
int
smb_ascii_or_unicode_strlen(struct smb_request *sr, char *str)
{
- if (sr->smb_flg2 & SMB_FLAGS2_UNICODE)
+ if (sr->session->dialect >= SMB_VERS_2_BASE ||
+ (sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0)
return (smb_wcequiv_strlen(str));
return (strlen(str));
}
@@ -81,7 +82,8 @@ smb_ascii_or_unicode_strlen(struct smb_request *sr, char *str)
int
smb_ascii_or_unicode_strlen_null(struct smb_request *sr, char *str)
{
- if (sr->smb_flg2 & SMB_FLAGS2_UNICODE)
+ if (sr->session->dialect >= SMB_VERS_2_BASE ||
+ (sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0)
return (smb_wcequiv_strlen(str) + 2);
return (strlen(str) + 1);
}
@@ -89,7 +91,8 @@ smb_ascii_or_unicode_strlen_null(struct smb_request *sr, char *str)
int
smb_ascii_or_unicode_null_len(struct smb_request *sr)
{
- if (sr->smb_flg2 & SMB_FLAGS2_UNICODE)
+ if (sr->session->dialect >= SMB_VERS_2_BASE ||
+ (sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0)
return (2);
return (1);
}
@@ -180,18 +183,17 @@ smb_sattr_check(uint16_t dosattr, uint16_t sattr)
return (B_TRUE);
}
-int
-microtime(timestruc_t *tvp)
+time_t
+smb_get_boottime(void)
{
- tvp->tv_sec = gethrestime_sec();
- tvp->tv_nsec = 0;
- return (0);
-}
+ extern time_t boot_time;
+ zone_t *z = curzone;
-int32_t
-clock_get_milli_uptime()
-{
- return (TICK_TO_MSEC(ddi_get_lbolt()));
+ /* Unfortunately, the GZ doesn't set zone_boot_time. */
+ if (z->zone_id == GLOBAL_ZONEID)
+ return (boot_time);
+
+ return (z->zone_boot_time);
}
/*
diff --git a/usr/src/uts/common/fs/smbsrv/smb_lock.c b/usr/src/uts/common/fs/smbsrv/smb_lock.c
index d86dd76604..cb069d28b0 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_lock.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_lock.c
@@ -148,7 +148,8 @@ smb_lock_range(
smb_lock_t *lock;
smb_lock_t *clock = NULL;
uint32_t result = NT_STATUS_SUCCESS;
- boolean_t lock_has_timeout = (timeout != 0);
+ boolean_t lock_has_timeout =
+ (timeout != 0 && timeout != UINT_MAX);
lock = smb_lock_create(sr, start, length, locktype, timeout);
@@ -194,8 +195,10 @@ smb_lock_range(
/*
* Under certain conditions NT_STATUS_FILE_LOCK_CONFLICT
* should be returned instead of NT_STATUS_LOCK_NOT_GRANTED.
+ * All of this appears to be specific to SMB1
*/
- if (result == NT_STATUS_LOCK_NOT_GRANTED) {
+ if (sr->session->dialect <= NT_LM_0_12 &&
+ result == NT_STATUS_LOCK_NOT_GRANTED) {
/*
* Locks with timeouts always return
* NT_STATUS_FILE_LOCK_CONFLICT
diff --git a/usr/src/uts/common/fs/smbsrv/smb_locking_andx.c b/usr/src/uts/common/fs/smbsrv/smb_locking_andx.c
index 51979a0c59..a9400b5a17 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_locking_andx.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_locking_andx.c
@@ -234,7 +234,7 @@ smb_com_locking_andx(smb_request_t *sr)
uint32_t timeout; /* Milliseconds to wait for lock */
unsigned short unlock_num; /* # unlock range structs */
unsigned short lock_num; /* # lock range structs */
- unsigned short pid; /* Process Id of owner */
+ uint32_t save_pid; /* Process Id of owner */
uint32_t offset32, length32;
uint64_t offset64;
uint64_t length64;
@@ -242,6 +242,7 @@ smb_com_locking_andx(smb_request_t *sr)
int rc;
uint32_t ltype;
smb_ofile_t *ofile;
+ uint16_t tmp_pid; /* locking uses 16-bit pids */
uint8_t brk;
rc = smbsr_decode_vwv(sr, "4.wbblww", &sr->smb_fid, &lock_type,
@@ -266,7 +267,7 @@ smb_com_locking_andx(smb_request_t *sr)
else
ltype = SMB_LOCK_TYPE_READWRITE;
- pid = sr->smb_pid; /* Save the original pid */
+ save_pid = sr->smb_pid; /* Save the original pid */
if (lock_type & LOCKING_ANDX_OPLOCK_RELEASE) {
if (oplock_level == 0)
@@ -309,7 +310,7 @@ smb_com_locking_andx(smb_request_t *sr)
for (i = 0; i < unlock_num; i++) {
rc = smb_mbc_decodef(&sr->smb_data, "w2.QQ",
- &sr->smb_pid, &offset64, &length64);
+ &tmp_pid, &offset64, &length64);
if (rc) {
/*
* This is the error returned by Windows 2000
@@ -318,6 +319,7 @@ smb_com_locking_andx(smb_request_t *sr)
smbsr_error(sr, 0, ERRSRV, ERRerror);
return (SDRC_ERROR);
}
+ sr->smb_pid = tmp_pid; /* NB: 16-bit */
result = smb_unlock_range(sr, sr->fid_ofile->f_node,
offset64, length64);
@@ -330,11 +332,12 @@ smb_com_locking_andx(smb_request_t *sr)
for (i = 0; i < lock_num; i++) {
rc = smb_mbc_decodef(&sr->smb_data, "w2.QQ",
- &sr->smb_pid, &offset64, &length64);
+ &tmp_pid, &offset64, &length64);
if (rc) {
smbsr_error(sr, 0, ERRSRV, ERRerror);
return (SDRC_ERROR);
}
+ sr->smb_pid = tmp_pid; /* NB: 16-bit */
result = smb_lock_range(sr, offset64, length64, timeout,
ltype);
@@ -345,12 +348,13 @@ smb_com_locking_andx(smb_request_t *sr)
}
} else {
for (i = 0; i < unlock_num; i++) {
- rc = smb_mbc_decodef(&sr->smb_data, "wll", &sr->smb_pid,
+ rc = smb_mbc_decodef(&sr->smb_data, "wll", &tmp_pid,
&offset32, &length32);
if (rc) {
smbsr_error(sr, 0, ERRSRV, ERRerror);
return (SDRC_ERROR);
}
+ sr->smb_pid = tmp_pid; /* NB: 16-bit */
result = smb_unlock_range(sr, sr->fid_ofile->f_node,
(uint64_t)offset32, (uint64_t)length32);
@@ -362,12 +366,13 @@ smb_com_locking_andx(smb_request_t *sr)
}
for (i = 0; i < lock_num; i++) {
- rc = smb_mbc_decodef(&sr->smb_data, "wll", &sr->smb_pid,
+ rc = smb_mbc_decodef(&sr->smb_data, "wll", &tmp_pid,
&offset32, &length32);
if (rc) {
smbsr_error(sr, 0, ERRSRV, ERRerror);
return (SDRC_ERROR);
}
+ sr->smb_pid = tmp_pid; /* NB: 16-bit */
result = smb_lock_range(sr, (uint64_t)offset32,
(uint64_t)length32, timeout, ltype);
@@ -378,8 +383,58 @@ smb_com_locking_andx(smb_request_t *sr)
}
}
- sr->smb_pid = pid;
+ sr->smb_pid = save_pid;
if (smbsr_encode_result(sr, 2, 0, "bb.ww", 2, sr->andx_com, 7, 0))
return (SDRC_ERROR);
return (SDRC_SUCCESS);
}
+
+/*
+ * Compose an SMB1 Oplock Break Notification packet, including
+ * the SMB1 header and everything, in sr->reply.
+ * The caller will send it and free the request.
+ */
+void
+smb1_oplock_break_notification(smb_request_t *sr, uint8_t brk)
+{
+ smb_ofile_t *ofile = sr->fid_ofile;
+ uint16_t fid;
+ uint8_t lock_type;
+ uint8_t oplock_level;
+
+ switch (brk) {
+ default:
+ ASSERT(0);
+ /* FALLTHROUGH */
+ case SMB_OPLOCK_BREAK_TO_NONE:
+ oplock_level = 0;
+ break;
+ case SMB_OPLOCK_BREAK_TO_LEVEL_II:
+ oplock_level = 1;
+ break;
+ }
+
+ sr->smb_com = SMB_COM_LOCKING_ANDX;
+ sr->smb_tid = ofile->f_tree->t_tid;
+ sr->smb_pid = 0xFFFF;
+ sr->smb_uid = 0;
+ sr->smb_mid = 0xFFFF;
+ fid = ofile->f_fid;
+ lock_type = LOCKING_ANDX_OPLOCK_RELEASE;
+
+ (void) smb_mbc_encodef(
+ &sr->reply, "Mb19.wwwwbb3.wbb10.",
+ /* "\xffSMB" M */
+ sr->smb_com, /* b */
+ /* status, flags, signature 19. */
+ sr->smb_tid, /* w */
+ sr->smb_pid, /* w */
+ sr->smb_uid, /* w */
+ sr->smb_mid, /* w */
+ 8, /* word count b */
+ 0xFF, /* AndX cmd b */
+ /* AndX reserved, offset 3. */
+ fid,
+ lock_type,
+ oplock_level);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c
index cf59d6eea2..a3fa7f364f 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c
@@ -22,7 +22,7 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -86,7 +86,10 @@ static int mbc_marshal_get_skip(mbuf_chain_t *, uint_t);
* specified (number preceding m).
*
* M Read the 32 bit value at the current location of the mbuf chain
- * and check if it matches the signature of an SMB request (SMBX).
+ * and check if it matches the signature of an SMB1 request (SMBx).
+ *
+ * N Read the 32 bit value at the current location of the mbuf chain
+ * and check if it matches the signature of an SMB2 request (SMBx).
*
* b Pointer to a buffer. Copy to that buffer the number of bytes
* specified (number preceding b).
@@ -144,7 +147,7 @@ static int mbc_marshal_get_skip(mbuf_chain_t *, uint_t);
* , Same as '.' but take in account it is an unicode string.
*/
int
-smb_mbc_vdecodef(mbuf_chain_t *mbc, char *fmt, va_list ap)
+smb_mbc_vdecodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
{
uint8_t c;
uint8_t cval;
@@ -181,6 +184,10 @@ smb_mbc_vdecodef(mbuf_chain_t *mbc, char *fmt, va_list ap)
switch (c) {
case '%':
sr = va_arg(ap, struct smb_request *);
+ if (sr->session->dialect >= SMB_VERS_2_BASE) {
+ unicode = 1;
+ break;
+ }
unicode = sr->smb_flg2 & SMB_FLAGS2_UNICODE;
break;
@@ -198,13 +205,18 @@ smb_mbc_vdecodef(mbuf_chain_t *mbc, char *fmt, va_list ap)
case 'M':
if (mbc_marshal_get_long(mbc, &lval) != 0)
- /* Data will never be available */
return (-1);
-
if (lval != 0x424D53FF) /* 0xFF S M B */
return (-1);
break;
+ case 'N':
+ if (mbc_marshal_get_long(mbc, &lval) != 0)
+ return (-1);
+ if (lval != 0x424D53FE) /* 0xFE S M B */
+ return (-1);
+ break;
+
case 'b':
case 'c':
cvalp = va_arg(ap, uint8_t *);
@@ -379,7 +391,7 @@ unicode_translation:
* (for a description of the format string see smb_mbc_vencodef()).
*/
int
-smb_mbc_decodef(mbuf_chain_t *mbc, char *fmt, ...)
+smb_mbc_decodef(mbuf_chain_t *mbc, const char *fmt, ...)
{
int xx;
va_list ap;
@@ -400,7 +412,7 @@ smb_mbc_decodef(mbuf_chain_t *mbc, char *fmt, ...)
* (for a description of the format string see smb_mbc_vdecodef()).
*/
int
-smb_mbc_peek(mbuf_chain_t *mbc, int offset, char *fmt, ...)
+smb_mbc_peek(mbuf_chain_t *mbc, int offset, const char *fmt, ...)
{
mbuf_chain_t tmp;
va_list ap;
@@ -437,7 +449,9 @@ smb_mbc_peek(mbuf_chain_t *mbc, int offset, char *fmt, ...)
* by that structure into the mbuf chain. The tag field is hard
* coded to '1'.
*
- * M Write the SMB request signature ('SMBX') into the mbuf chain.
+ * M Write the SMB1 request signature ('SMBX') into the mbuf chain.
+ *
+ * N Write the SMB2 request signature ('SMBX') into the mbuf chain.
*
* T Pointer to a timestruc_t. Convert the content of the structure
* into NT time and store the result of the conversion in the
@@ -503,7 +517,7 @@ smb_mbc_peek(mbuf_chain_t *mbc, int offset, char *fmt, ...)
* U Align the offset of the mbuf chain on a 16bit boundary.
*/
int
-smb_mbc_vencodef(mbuf_chain_t *mbc, char *fmt, va_list ap)
+smb_mbc_vencodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
{
uint8_t *cvalp;
timestruc_t *tvp;
@@ -541,6 +555,10 @@ smb_mbc_vencodef(mbuf_chain_t *mbc, char *fmt, va_list ap)
switch (c) {
case '%':
sr = va_arg(ap, struct smb_request *);
+ if (sr->session->dialect >= SMB_VERS_2_BASE) {
+ unicode = 1;
+ break;
+ }
unicode = sr->smb_flg2 & SMB_FLAGS2_UNICODE;
break;
@@ -567,6 +585,12 @@ smb_mbc_vencodef(mbuf_chain_t *mbc, char *fmt, va_list ap)
return (DECODE_NO_MORE_DATA);
break;
+ case 'N':
+ /* 0xFE S M B */
+ if (mbc_marshal_put_long(mbc, 0x424D53FE))
+ return (DECODE_NO_MORE_DATA);
+ break;
+
case 'T':
tvp = va_arg(ap, timestruc_t *);
nt_time = smb_time_unix_to_nt(tvp);
@@ -733,7 +757,7 @@ unicode_translation:
* (for a description of the format string see smb_mbc_vencodef()).
*/
int
-smb_mbc_encodef(mbuf_chain_t *mbc, char *fmt, ...)
+smb_mbc_encodef(mbuf_chain_t *mbc, const char *fmt, ...)
{
int rc;
va_list ap;
@@ -754,17 +778,23 @@ smb_mbc_encodef(mbuf_chain_t *mbc, char *fmt, ...)
* (for a description of the format string see smb_mbc_vencodef()).
*/
int
-smb_mbc_poke(mbuf_chain_t *mbc, int offset, char *fmt, ...)
+smb_mbc_poke(mbuf_chain_t *mbc, int offset, const char *fmt, ...)
{
- int xx;
+ int len, rc;
mbuf_chain_t tmp;
va_list ap;
- (void) MBC_SHADOW_CHAIN(&tmp, mbc, offset, mbc->max_bytes - offset);
+ if ((len = mbc->max_bytes - offset) < 0)
+ return (DECODE_NO_MORE_DATA);
+ rc = MBC_SHADOW_CHAIN(&tmp, mbc, offset, len);
+ if (rc)
+ return (DECODE_NO_MORE_DATA);
+
va_start(ap, fmt);
- xx = smb_mbc_vencodef(&tmp, fmt, ap);
+ rc = smb_mbc_vencodef(&tmp, fmt, ap);
va_end(ap);
- return (xx);
+
+ return (rc);
}
/*
@@ -907,8 +937,7 @@ mbc_marshal_make_room(mbuf_chain_t *mbc, int32_t bytes_needed)
if ((m = mbc->chain) == 0) {
MGET(m, M_WAIT, MT_DATA);
m->m_len = 0;
- if (mbc->max_bytes > MLEN)
- MCLGET(m, M_WAIT);
+ MCLGET(m, M_WAIT);
mbc->chain = m;
/* xxxx */
/* ^ */
@@ -952,11 +981,10 @@ mbc_marshal_make_room(mbuf_chain_t *mbc, int32_t bytes_needed)
MGET(m->m_next, M_WAIT, MT_DATA);
m = m->m_next;
m->m_len = 0;
- if (bytes_needed > MLEN)
- MCLGET(m, M_WAIT);
+ MCLGET(m, M_WAIT);
- bytes_available = (m->m_flags & M_EXT) ?
- m->m_ext.ext_size : MLEN;
+ ASSERT((m->m_flags & M_EXT) != 0);
+ bytes_available = m->m_ext.ext_size;
/* ---- ----- --xx ------ xxxx */
/* ^ */
@@ -1115,6 +1143,12 @@ mbc_marshal_put_unicode_string(mbuf_chain_t *mbc, char *ascii, int repc)
return (0);
}
+static int /*ARGSUSED*/
+uiorefnoop(caddr_t p, int size, int adj)
+{
+ return (0);
+}
+
static int
mbc_marshal_put_uio(mbuf_chain_t *mbc, struct uio *uio)
{
@@ -1128,7 +1162,7 @@ mbc_marshal_put_uio(mbuf_chain_t *mbc, struct uio *uio)
for (i = 0; i < iov_cnt; i++) {
MGET(m, M_WAIT, MT_DATA);
m->m_ext.ext_buf = iov->iov_base;
- m->m_ext.ext_ref = mclrefnoop;
+ m->m_ext.ext_ref = uiorefnoop;
m->m_data = m->m_ext.ext_buf;
m->m_flags |= M_EXT;
m->m_len = m->m_ext.ext_size = iov->iov_len;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_mbuf_util.c b/usr/src/uts/common/fs/smbsrv/smb_mbuf_util.c
index 99b46cd5f3..86d37c8adb 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_mbuf_util.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_mbuf_util.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -62,6 +62,8 @@
#include <smbsrv/smb_kstat.h>
static kmem_cache_t *smb_mbc_cache = NULL;
+static kmem_cache_t *smb_mbuf_cache = NULL;
+static kmem_cache_t *smb_mbufcl_cache = NULL;
void
smb_mbc_init(void)
@@ -70,6 +72,12 @@ smb_mbc_init(void)
return;
smb_mbc_cache = kmem_cache_create(SMBSRV_KSTAT_MBC_CACHE,
sizeof (mbuf_chain_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
+
+ smb_mbuf_cache = kmem_cache_create("smb_mbuf_cache",
+ sizeof (mbuf_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
+
+ smb_mbufcl_cache = kmem_cache_create("smb_mbufcl_cache",
+ MCLBYTES, 8, NULL, NULL, NULL, NULL, NULL, 0);
}
void
@@ -79,6 +87,14 @@ smb_mbc_fini(void)
kmem_cache_destroy(smb_mbc_cache);
smb_mbc_cache = NULL;
}
+ if (smb_mbuf_cache != NULL) {
+ kmem_cache_destroy(smb_mbuf_cache);
+ smb_mbuf_cache = NULL;
+ }
+ if (smb_mbufcl_cache != NULL) {
+ kmem_cache_destroy(smb_mbufcl_cache);
+ smb_mbufcl_cache = NULL;
+ }
}
mbuf_chain_t *
@@ -150,45 +166,47 @@ smb_mbuf_get(uchar_t *buf, int nbytes)
return (mhead);
}
+static int
+smb_mbuf_kmem_ref(void *p, uint_t sz, int incr)
+{
+ if (incr < 0)
+ kmem_free(p, sz);
+ return (0);
+}
+
/*
- * Allocate enough mbufs to accommodate the residual count in a uio.
+ * Allocate enough mbufs to accommodate the residual count in uio,
+ * and setup the uio_iov to point to them.
+ *
+ * This is used by the various SMB read code paths. That code is
+ * going to do a disk read into this buffer, so we'd like it to be
+ * large and contiguous. Use an external (M_EXT) buffer.
*/
struct mbuf *
smb_mbuf_allocate(struct uio *uio)
{
- struct iovec *iovp;
- struct mbuf *mhead = 0;
- struct mbuf *m = 0;
- int count, iovs, resid;
-
- iovp = uio->uio_iov;
- iovs = uio->uio_iovcnt;
- resid = uio->uio_resid;
-
- while ((resid > 0) && (iovs > 0)) {
- count = (resid > MCLBYTES) ? MCLBYTES : resid;
- resid -= count;
-
- if (mhead == 0) {
- MGET(mhead, M_WAIT, MT_DATA);
- m = mhead;
- } else {
- MGET(m->m_next, M_WAIT, MT_DATA);
- m = m->m_next;
- }
-
- if (count > MLEN) {
- MCLGET(m, M_WAIT);
- }
-
- iovp->iov_base = m->m_data;
- iovp->iov_len = m->m_len = count;
- iovs--;
- iovp++;
+ mbuf_t *m = 0;
+ int len = uio->uio_resid;
+
+ MGET(m, M_WAIT, MT_DATA);
+ if (len > MCLBYTES) {
+ /* Like MCLGET(), but bigger buf. */
+ m->m_ext.ext_buf = kmem_zalloc(len, KM_SLEEP);
+ m->m_data = m->m_ext.ext_buf;
+ m->m_flags |= M_EXT;
+ m->m_ext.ext_size = len;
+ m->m_ext.ext_ref = smb_mbuf_kmem_ref;
+ } else if (len > MLEN) {
+ /* Use the kmem cache. */
+ MCLGET(m, M_WAIT);
}
+ m->m_len = len;
- uio->uio_iovcnt -= iovs;
- return (mhead);
+ uio->uio_iov->iov_base = m->m_data;
+ uio->uio_iov->iov_len = m->m_len;
+ uio->uio_iovcnt = 1;
+
+ return (m);
}
/*
@@ -295,6 +313,11 @@ MBC_APPEND_MBUF(struct mbuf_chain *MBC, struct mbuf *MBUF)
}
}
+static int /*ARGSUSED*/
+mclrefnoop(caddr_t p, int size, int adj)
+{
+ return (0);
+}
void
MBC_ATTACH_BUF(struct mbuf_chain *MBC, unsigned char *BUF, int LEN)
@@ -312,48 +335,22 @@ MBC_ATTACH_BUF(struct mbuf_chain *MBC, unsigned char *BUF, int LEN)
int
-MBC_SHADOW_CHAIN(struct mbuf_chain *SUBMBC, struct mbuf_chain *MBC,
- int OFF, int LEN)
+MBC_SHADOW_CHAIN(struct mbuf_chain *submbc, struct mbuf_chain *mbc,
+ int off, int len)
{
- if (((OFF) + (LEN)) > (MBC)->max_bytes)
+ int x = off + len;
+
+ if (off < 0 || len < 0 || x < 0 ||
+ off > mbc->max_bytes || x > mbc->max_bytes)
return (EMSGSIZE);
- *(SUBMBC) = *(MBC);
- (SUBMBC)->chain_offset = (OFF);
- (SUBMBC)->max_bytes = (OFF) + (LEN);
- (SUBMBC)->shadow_of = (MBC);
+ *submbc = *mbc;
+ submbc->chain_offset = off;
+ submbc->max_bytes = x;
+ submbc->shadow_of = mbc;
return (0);
}
-int
-mbc_moveout(mbuf_chain_t *mbc, caddr_t buf, int buflen, int *tlen)
-{
- int rc = 0;
- int len = 0;
-
- if ((mbc != NULL) && (mbc->chain != NULL)) {
- mbuf_t *m;
-
- m = mbc->chain;
- while (m) {
- if ((len + m->m_len) <= buflen) {
- bcopy(m->m_data, buf, m->m_len);
- buf += m->m_len;
- len += m->m_len;
- m = m->m_next;
- continue;
- }
- rc = EMSGSIZE;
- break;
- }
- m_freem(mbc->chain);
- mbc->chain = NULL;
- mbc->flags = 0;
- }
- *tlen = len;
- return (rc);
-}
-
/*
* Free a single mbuf structure. Calls m->m_ext.ext_ref() to free any
* associated external buffers if present (indicated by m->m_flags & M_EXT)
@@ -392,15 +389,43 @@ m_freem(struct mbuf *m)
* Mbuffer utility routines.
*/
-int /*ARGSUSED*/
-mclref(caddr_t p, int size, int adj) /* size, adj are unused */
+mbuf_t *
+smb_mbuf_alloc(void)
{
- MEM_FREE("mbuf", p);
- return (0);
+ mbuf_t *m;
+
+ m = kmem_cache_alloc(smb_mbuf_cache, KM_SLEEP);
+ bzero(m, sizeof (*m));
+ return (m);
}
-int /*ARGSUSED*/
-mclrefnoop(caddr_t p, int size, int adj) /* p, size, adj are unused */
+void
+smb_mbuf_free(mbuf_t *m)
+{
+ kmem_cache_free(smb_mbuf_cache, m);
+}
+
+void *
+smb_mbufcl_alloc(void)
+{
+ void *p;
+
+ p = kmem_cache_alloc(smb_mbufcl_cache, KM_SLEEP);
+ bzero(p, MCLBYTES);
+ return (p);
+}
+
+void
+smb_mbufcl_free(void *p)
+{
+ kmem_cache_free(smb_mbufcl_cache, p);
+}
+
+int
+smb_mbufcl_ref(void *p, uint_t sz, int incr)
{
+ ASSERT3S(sz, ==, MCLBYTES);
+ if (incr < 0)
+ kmem_cache_free(smb_mbufcl_cache, p);
return (0);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_negotiate.c b/usr/src/uts/common/fs/smbsrv/smb_negotiate.c
index 08597f79ce..8750b30810 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_negotiate.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_negotiate.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 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -203,8 +203,11 @@ static const smb_xlate_t smb_dialect[] = {
{ DOS_LANMAN2_1, "DOS LANMAN2.1" },
{ LANMAN2_1, "LANMAN2.1" },
{ Windows_for_Workgroups_3_1a, "Windows for Workgroups 3.1a" },
- { NT_LM_0_12, "NT LM 0.12" }
+ { NT_LM_0_12, "NT LM 0.12" },
+ { DIALECT_SMB2002, "SMB 2.002" },
+ { DIALECT_SMB2XXX, "SMB 2.???" },
};
+static int smb_ndialects = sizeof (smb_dialect) / sizeof (smb_dialect[0]);
/*
* Maximum buffer size for DOS: chosen to be the same as NT.
@@ -227,6 +230,15 @@ static const smb_xlate_t smb_dialect[] = {
static uint32_t smb_dos_tcp_rcvbuf = 8700;
static uint32_t smb_nt_tcp_rcvbuf = 1048560; /* scale factor of 4 */
+/*
+ * Maximum number of simultaneously pending SMB requests allowed on
+ * one connection. This is like "credits" in SMB2, but SMB1 uses a
+ * fixed limit, having no way to request an increase like SMB2 does.
+ * Note: Some older clients only handle the low byte of this value,
+ * so this value should be less than 256.
+ */
+static uint16_t smb_maxmpxcount = 64;
+
static int smb_xlate_dialect(const char *);
/*
@@ -255,14 +267,94 @@ uint32_t smb1srv_capabilities =
CAP_LARGE_WRITEX |
CAP_EXTENDED_SECURITY;
+/*
+ * SMB Negotiate gets special handling. This is called directly by
+ * the reader thread (see smbsr_newrq_initial) with what _should_ be
+ * an SMB1 Negotiate. Only the "\ffSMB" header has been checked
+ * when this is called, so this needs to check the SMB command,
+ * if it's Negotiate execute it, then send the reply, etc.
+ *
+ * Since this is called directly from the reader thread, we
+ * know this is the only thread currently using this session.
+ * This has to duplicate some of what smb1sr_work does as a
+ * result of bypassing the normal dispatch mechanism.
+ *
+ * The caller always frees this request.
+ */
+int
+smb1_newrq_negotiate(smb_request_t *sr)
+{
+ smb_sdrc_t sdrc;
+ uint16_t pid_hi, pid_lo;
+
+ /*
+ * Decode the header
+ */
+ if (smb_mbc_decodef(&sr->command, SMB_HEADER_ED_FMT,
+ &sr->smb_com,
+ &sr->smb_rcls,
+ &sr->smb_reh,
+ &sr->smb_err,
+ &sr->smb_flg,
+ &sr->smb_flg2,
+ &pid_hi,
+ sr->smb_sig,
+ &sr->smb_tid,
+ &pid_lo,
+ &sr->smb_uid,
+ &sr->smb_mid) != 0)
+ return (-1);
+ if (sr->smb_com != SMB_COM_NEGOTIATE)
+ return (-1);
+
+ sr->smb_pid = (pid_hi << 16) | pid_lo;
+
+ /*
+ * Reserve space for the reply header.
+ */
+ (void) smb_mbc_encodef(&sr->reply, "#.", SMB_HEADER_LEN);
+ sr->first_smb_com = sr->smb_com;
+
+ if (smb_mbc_decodef(&sr->command, "b", &sr->smb_wct) != 0)
+ return (-1);
+ (void) MBC_SHADOW_CHAIN(&sr->smb_vwv, &sr->command,
+ sr->command.chain_offset, sr->smb_wct * 2);
+
+ if (smb_mbc_decodef(&sr->command, "#.w", sr->smb_wct*2, &sr->smb_bcc))
+ return (-1);
+ (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
+ sr->command.chain_offset, sr->smb_bcc);
+
+ sr->command.chain_offset += sr->smb_bcc;
+ if (sr->command.chain_offset > sr->command.max_bytes)
+ return (-1);
+
+ /* Store pointers for later */
+ sr->cur_reply_offset = sr->reply.chain_offset;
+
+ sdrc = smb_pre_negotiate(sr);
+ if (sdrc == SDRC_SUCCESS)
+ sdrc = smb_com_negotiate(sr);
+ smb_post_negotiate(sr);
+
+ if (sdrc != SDRC_NO_REPLY)
+ smbsr_send_reply(sr);
+ if (sdrc == SDRC_DROP_VC)
+ return (-1);
+
+ return (0);
+}
+
smb_sdrc_t
smb_pre_negotiate(smb_request_t *sr)
{
+ smb_kmod_cfg_t *skc;
smb_arg_negotiate_t *negprot;
int dialect;
int pos;
int rc = 0;
+ skc = &sr->session->s_cfg;
negprot = smb_srm_zalloc(sr, sizeof (smb_arg_negotiate_t));
negprot->ni_index = -1;
sr->sr_negprot = negprot;
@@ -277,6 +369,13 @@ smb_pre_negotiate(smb_request_t *sr)
if ((dialect = smb_xlate_dialect(negprot->ni_name)) < 0)
continue;
+ /*
+ * Conditionally recognize the SMB2 dialects.
+ */
+ if (dialect >= DIALECT_SMB2002 &&
+ skc->skc_max_protocol < SMB_VERS_2_BASE)
+ continue;
+
if (negprot->ni_dialect < dialect) {
negprot->ni_dialect = dialect;
negprot->ni_index = pos;
@@ -285,7 +384,7 @@ smb_pre_negotiate(smb_request_t *sr)
DTRACE_SMB_2(op__Negotiate__start, smb_request_t *, sr,
smb_arg_negotiate_t, negprot);
- smb_rwx_rwenter(&sr->session->s_lock, RW_WRITER);
+
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
@@ -296,7 +395,6 @@ smb_post_negotiate(smb_request_t *sr)
DTRACE_SMB_2(op__Negotiate__done, smb_request_t *, sr,
smb_arg_negotiate_t, negprot);
- smb_rwx_rwexit(&sr->session->s_lock);
bzero(negprot, sizeof (smb_arg_negotiate_t));
}
@@ -304,9 +402,9 @@ smb_post_negotiate(smb_request_t *sr)
smb_sdrc_t
smb_com_negotiate(smb_request_t *sr)
{
+ smb_session_t *session = sr->session;
smb_arg_negotiate_t *negprot = sr->sr_negprot;
uint16_t secmode;
- uint16_t rawmode = 0;
uint32_t sesskey;
char *nbdomain;
uint8_t *wcbuf;
@@ -314,29 +412,48 @@ smb_com_negotiate(smb_request_t *sr)
smb_msgbuf_t mb;
int rc;
- if (sr->session->s_state != SMB_SESSION_STATE_ESTABLISHED) {
+ if (session->s_state != SMB_SESSION_STATE_ESTABLISHED) {
/* The protocol has already been negotiated. */
smbsr_error(sr, 0, ERRSRV, ERRerror);
return (SDRC_ERROR);
}
- sr->session->secmode = NEGOTIATE_ENCRYPT_PASSWORDS |
+ /*
+ * Special case for negotiating SMB2 from SMB1. The client
+ * includes the "SMB 2..." dialects in the SMB1 negotiate,
+ * and if SMB2 is enabled, we choose one of those and then
+ * send an SMB2 reply to that SMB1 request. Yes, it's very
+ * strange, but this SMB1 request can have an SMB2 reply!
+ * To accomplish this, we let the SMB2 code send the reply
+ * and return the special code SDRC_NO_REPLY to the SMB1
+ * dispatch logic so it will NOT send an SMB1 reply.
+ * (Or possibly send an SMB1 error reply.)
+ */
+ if (negprot->ni_dialect >= DIALECT_SMB2002) {
+ rc = smb1_negotiate_smb2(sr);
+ ASSERT(rc == SDRC_NO_REPLY ||
+ rc == SDRC_DROP_VC || rc == SDRC_ERROR);
+ return (rc);
+ }
+
+ session->secmode = NEGOTIATE_ENCRYPT_PASSWORDS |
NEGOTIATE_USER_SECURITY;
- secmode = sr->session->secmode;
- sesskey = sr->session->sesskey;
+ secmode = session->secmode;
+ sesskey = session->sesskey;
- (void) microtime(&negprot->ni_servertime);
+ negprot->ni_servertime.tv_sec = gethrestime_sec();
+ negprot->ni_servertime.tv_nsec = 0;
negprot->ni_tzcorrection = sr->sr_gmtoff / 60;
- negprot->ni_maxmpxcount = sr->sr_cfg->skc_maxworkers;
+ negprot->ni_maxmpxcount = smb_maxmpxcount;
negprot->ni_keylen = SMB_CHALLENGE_SZ;
- bcopy(&sr->session->challenge_key, negprot->ni_key, SMB_CHALLENGE_SZ);
+ bcopy(&session->challenge_key, negprot->ni_key, SMB_CHALLENGE_SZ);
nbdomain = sr->sr_cfg->skc_nbdomain;
negprot->ni_capabilities = smb1srv_capabilities;
switch (negprot->ni_dialect) {
case PC_NETWORK_PROGRAM_1_0: /* core */
- (void) ksocket_setsockopt(sr->session->sock, SOL_SOCKET,
+ (void) ksocket_setsockopt(session->sock, SOL_SOCKET,
SO_RCVBUF, (const void *)&smb_dos_tcp_rcvbuf,
sizeof (smb_dos_tcp_rcvbuf), CRED());
rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
@@ -350,7 +467,7 @@ smb_com_negotiate(smb_request_t *sr)
case LANMAN1_0:
case LM1_2X002:
case DOS_LM1_2X002:
- (void) ksocket_setsockopt(sr->session->sock, SOL_SOCKET,
+ (void) ksocket_setsockopt(session->sock, SOL_SOCKET,
SO_RCVBUF, (const void *)&smb_dos_tcp_rcvbuf,
sizeof (smb_dos_tcp_rcvbuf), CRED());
sr->smb_flg |= SMB_FLAGS_LOCK_AND_READ_OK;
@@ -362,7 +479,7 @@ smb_com_negotiate(smb_request_t *sr)
SMB_DOS_MAXBUF, /* max buffer size */
1, /* max MPX */
1, /* max VCs */
- rawmode, /* read/write raw (s/b 3) */
+ 0, /* read/write raw */
sesskey, /* session key */
negprot->ni_servertime.tv_sec, /* server date/time */
negprot->ni_tzcorrection,
@@ -375,7 +492,7 @@ smb_com_negotiate(smb_request_t *sr)
case DOS_LANMAN2_1:
case LANMAN2_1:
- (void) ksocket_setsockopt(sr->session->sock, SOL_SOCKET,
+ (void) ksocket_setsockopt(session->sock, SOL_SOCKET,
SO_RCVBUF, (const void *)&smb_dos_tcp_rcvbuf,
sizeof (smb_dos_tcp_rcvbuf), CRED());
sr->smb_flg |= SMB_FLAGS_LOCK_AND_READ_OK;
@@ -387,7 +504,7 @@ smb_com_negotiate(smb_request_t *sr)
SMB_DOS_MAXBUF, /* max buffer size */
1, /* max MPX */
1, /* max VCs */
- rawmode, /* read/write raw (s/b 3) */
+ 0, /* read/write raw */
sesskey, /* session key */
negprot->ni_servertime.tv_sec, /* server date/time */
negprot->ni_tzcorrection,
@@ -400,7 +517,7 @@ smb_com_negotiate(smb_request_t *sr)
break;
case NT_LM_0_12:
- (void) ksocket_setsockopt(sr->session->sock, SOL_SOCKET,
+ (void) ksocket_setsockopt(session->sock, SOL_SOCKET,
SO_RCVBUF, (const void *)&smb_nt_tcp_rcvbuf,
sizeof (smb_nt_tcp_rcvbuf), CRED());
@@ -414,7 +531,7 @@ smb_com_negotiate(smb_request_t *sr)
secmode |=
NEGOTIATE_SECURITY_SIGNATURES_REQUIRED;
- sr->session->secmode = secmode;
+ session->secmode = secmode;
}
/*
@@ -495,18 +612,22 @@ NT_LM_0_12_ext_sec:
default:
rc = smbsr_encode_result(sr, 1, 0, "bww", 1, -1, 0);
- return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
+ break;
}
if (rc != 0)
return (SDRC_ERROR);
/*
- * Save the agreed dialect. Note that this value is also
+ * Save the agreed dialect. Note that the state is also
* used to detect and reject attempts to re-negotiate.
*/
- sr->session->dialect = negprot->ni_dialect;
- sr->session->s_state = SMB_SESSION_STATE_NEGOTIATED;
+ session->dialect = negprot->ni_dialect;
+ session->s_state = SMB_SESSION_STATE_NEGOTIATED;
+
+ /* Allow normal SMB1 requests now. */
+ session->newrq_func = smb1sr_newrq;
+
return (SDRC_SUCCESS);
}
@@ -516,7 +637,7 @@ smb_xlate_dialect(const char *dialect)
const smb_xlate_t *dp;
int i;
- for (i = 0; i < sizeof (smb_dialect) / sizeof (smb_dialect[0]); ++i) {
+ for (i = 0; i < smb_ndialects; ++i) {
dp = &smb_dialect[i];
if (strcmp(dp->str, dialect) == 0)
diff --git a/usr/src/uts/common/fs/smbsrv/smb_net.c b/usr/src/uts/common/fs/smbsrv/smb_net.c
index 9f63846537..bdcf2e0482 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_net.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_net.c
@@ -22,7 +22,7 @@
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -43,52 +43,6 @@
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_kstat.h>
-static kmem_cache_t *smb_txr_cache = NULL;
-
-/*
- * smb_net_init
- *
- * This function initializes the resources necessary to access the
- * network. It assumes it won't be called simultaneously by multiple
- * threads.
- *
- * Return Value
- *
- * 0 Initialization successful
- * ENOMEM Initialization failed
- */
-void
-smb_net_init(void)
-{
-
- if (smb_txr_cache != NULL)
- return;
-
- smb_txr_cache = kmem_cache_create(SMBSRV_KSTAT_TXRCACHE,
- sizeof (smb_txreq_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
-}
-
-/*
- * smb_net_fini
- *
- * This function releases the resources allocated by smb_net_init(). It
- * assumes it won't be called simultaneously by multiple threads.
- * This function can safely be called even if smb_net_init() hasn't been
- * called previously.
- *
- * Return Value
- *
- * None
- */
-void
-smb_net_fini(void)
-{
- if (smb_txr_cache) {
- kmem_cache_destroy(smb_txr_cache);
- smb_txr_cache = NULL;
- }
-}
-
/*
* SMB Network Socket API
*
@@ -170,8 +124,7 @@ smb_net_txl_constructor(smb_txlst_t *txl)
ASSERT(txl->tl_magic != SMB_TXLST_MAGIC);
mutex_init(&txl->tl_mutex, NULL, MUTEX_DEFAULT, NULL);
- list_create(&txl->tl_list, sizeof (smb_txreq_t),
- offsetof(smb_txreq_t, tr_lnd));
+ cv_init(&txl->tl_wait_cv, NULL, CV_DEFAULT, NULL);
txl->tl_active = B_FALSE;
txl->tl_magic = SMB_TXLST_MAGIC;
}
@@ -187,106 +140,71 @@ smb_net_txl_destructor(smb_txlst_t *txl)
ASSERT(txl->tl_magic == SMB_TXLST_MAGIC);
txl->tl_magic = 0;
- list_destroy(&txl->tl_list);
+ cv_destroy(&txl->tl_wait_cv);
mutex_destroy(&txl->tl_mutex);
}
/*
- * smb_net_txr_alloc
- *
- * Transmit buffer allocator
- */
-smb_txreq_t *
-smb_net_txr_alloc(void)
-{
- smb_txreq_t *txr;
-
- txr = kmem_cache_alloc(smb_txr_cache, KM_SLEEP);
- txr->tr_len = 0;
- bzero(&txr->tr_lnd, sizeof (txr->tr_lnd));
- txr->tr_magic = SMB_TXREQ_MAGIC;
- return (txr);
-}
-
-/*
- * smb_net_txr_free
+ * smb_net_send_uio
*
- * Transmit buffer deallocator
- */
-void
-smb_net_txr_free(smb_txreq_t *txr)
-{
- ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
- ASSERT(!list_link_active(&txr->tr_lnd));
-
- txr->tr_magic = 0;
- kmem_cache_free(smb_txr_cache, txr);
-}
-
-/*
- * smb_net_txr_send
- *
- * This routine puts the transmit buffer passed in on the wire. If another
- * thread is already draining the transmit list, the transmit buffer is
- * queued and the routine returns immediately.
+ * This routine puts the transmit buffer passed in on the wire.
+ * If another thread is already sending, block on the CV.
*/
int
-smb_net_txr_send(ksocket_t so, smb_txlst_t *txl, smb_txreq_t *txr)
+smb_net_send_uio(smb_session_t *s, struct uio *uio)
{
- list_t local;
- int rc = 0;
- size_t sent = 0;
- size_t len;
+ struct msghdr msg;
+ size_t sent;
+ smb_txlst_t *txl = &s->s_txlst;
+ int rc = 0;
- ASSERT(txl->tl_magic == SMB_TXLST_MAGIC);
+ DTRACE_PROBE1(send__wait__start, struct smb_session_t *, s);
+ /*
+ * Wait for our turn to send.
+ */
mutex_enter(&txl->tl_mutex);
- list_insert_tail(&txl->tl_list, txr);
- if (txl->tl_active) {
- mutex_exit(&txl->tl_mutex);
- return (0);
+ while (txl->tl_active)
+ cv_wait(&txl->tl_wait_cv, &txl->tl_mutex);
+
+ /*
+ * Did the connection close while we waited?
+ */
+ switch (s->s_state) {
+ case SMB_SESSION_STATE_DISCONNECTED:
+ case SMB_SESSION_STATE_TERMINATED:
+ rc = ENOTCONN;
+ break;
+ default:
+ txl->tl_active = B_TRUE;
+ break;
}
- txl->tl_active = B_TRUE;
-
- list_create(&local, sizeof (smb_txreq_t),
- offsetof(smb_txreq_t, tr_lnd));
-
- while (!list_is_empty(&txl->tl_list)) {
- list_move_tail(&local, &txl->tl_list);
- mutex_exit(&txl->tl_mutex);
- while ((txr = list_head(&local)) != NULL) {
- ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
- list_remove(&local, txr);
-
- len = txr->tr_len;
- rc = ksocket_send(so, txr->tr_buf, txr->tr_len,
- MSG_WAITALL, &sent, CRED());
- smb_net_txr_free(txr);
- if ((rc == 0) && (sent == len))
- continue;
-
- if (rc == 0)
- rc = -1;
+ mutex_exit(&txl->tl_mutex);
- while ((txr = list_head(&local)) != NULL) {
- ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
- list_remove(&local, txr);
- smb_net_txr_free(txr);
- }
+ DTRACE_PROBE1(send__wait__done, struct smb_session_t *, s);
+ if (rc != 0)
+ return (rc);
+
+ /*
+ * OK, try to send.
+ *
+ * This should block until we've sent it all,
+ * or given up due to errors (socket closed).
+ */
+ bzero(&msg, sizeof (msg));
+ msg.msg_iov = uio->uio_iov;
+ msg.msg_iovlen = uio->uio_iovcnt;
+ while (uio->uio_resid > 0) {
+ rc = ksocket_sendmsg(s->sock, &msg, 0, &sent, CRED());
+ if (rc != 0)
break;
- }
- mutex_enter(&txl->tl_mutex);
- if (rc == 0)
- continue;
-
- while ((txr = list_head(&txl->tl_list)) != NULL) {
- ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
- list_remove(&txl->tl_list, txr);
- smb_net_txr_free(txr);
- }
- break;
+ uio->uio_resid -= sent;
}
+
+ mutex_enter(&txl->tl_mutex);
txl->tl_active = B_FALSE;
+ cv_signal(&txl->tl_wait_cv);
mutex_exit(&txl->tl_mutex);
+
return (rc);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_node.c b/usr/src/uts/common/fs/smbsrv/smb_node.c
index 8ff046cb5d..58a58db092 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_node.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_node.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 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
* SMB Node State Machine
@@ -592,6 +592,7 @@ smb_node_root_init(smb_server_t *sv, smb_node_t **svrootp)
return (error);
}
+
/*
* Helper function for smb_node_set_delete_on_close(). Assumes node is a dir.
* Return 0 if this is an empty dir. Otherwise return a NT_STATUS code.
@@ -688,6 +689,13 @@ smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr, uint32_t flags)
node->flags |= NODE_FLAGS_DELETE_ON_CLOSE;
mutex_exit(&node->n_mutex);
+ /*
+ * Tell any change notify calls to close their handles
+ * and get out of the way. FILE_ACTION_DELETE_PENDING
+ * is a special, internal-only action for this purpose.
+ */
+ smb_notify_event(node, FILE_ACTION_DELETE_PENDING, NULL);
+
return (NT_STATUS_SUCCESS);
}
@@ -901,7 +909,7 @@ smb_node_notify_parents(smb_node_t *dnode)
while (pnode != NULL) {
SMB_NODE_VALID(pnode);
- smb_notify_event(pnode, 0, dnode->od_name);
+ smb_notify_event(pnode, FILE_ACTION_SUBDIR_CHANGED, NULL);
/* cd .. */
dnode = pnode;
pnode = dnode->n_dnode;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_notify.c b/usr/src/uts/common/fs/smbsrv/smb_notify.c
new file mode 100644
index 0000000000..75e7126613
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb_notify.c
@@ -0,0 +1,422 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * File Change Notification (FCN)
+ * Common parts shared by SMB1 & SMB2
+ */
+
+/*
+ * This command notifies the client when the specified directory
+ * has changed, and optionally returns the names of files and
+ * directories that changed, and how they changed. The caller
+ * specifies a "Completion Filter" to select which kinds of
+ * changes they want to know about.
+ *
+ * When a change that's in the CompletionFilter is made to the directory,
+ * the command completes. The names of the files that have changed since
+ * the last time the command was issued are returned to the client.
+ * If too many files have changed since the last time the command was
+ * issued, then zero bytes are returned and an alternate status code
+ * is returned in the Status field of the response.
+ *
+ * The CompletionFilter is a mask created as the sum of any of the
+ * following flags:
+ *
+ * FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001
+ * FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002
+ * FILE_NOTIFY_CHANGE_NAME 0x00000003
+ * FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004
+ * FILE_NOTIFY_CHANGE_SIZE 0x00000008
+ * FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010
+ * FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020
+ * FILE_NOTIFY_CHANGE_CREATION 0x00000040
+ * FILE_NOTIFY_CHANGE_EA 0x00000080
+ * FILE_NOTIFY_CHANGE_SECURITY 0x00000100
+ * FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200
+ * FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400
+ * FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800
+ *
+ *
+ * The response contains FILE_NOTIFY_INFORMATION structures, as defined
+ * below. The NextEntryOffset field of the structure specifies the offset,
+ * in bytes, from the start of the current entry to the next entry in the
+ * list. If this is the last entry in the list, this field is zero. Each
+ * entry in the list must be longword aligned, so NextEntryOffset must be a
+ * multiple of four.
+ *
+ * typedef struct {
+ * ULONG NextEntryOffset;
+ * ULONG Action;
+ * ULONG FileNameLength;
+ * WCHAR FileName[1];
+ * } FILE_NOTIFY_INFORMATION;
+ *
+ * Where Action describes what happened to the file named FileName:
+ *
+ * FILE_ACTION_ADDED 0x00000001
+ * FILE_ACTION_REMOVED 0x00000002
+ * FILE_ACTION_MODIFIED 0x00000003
+ * FILE_ACTION_RENAMED_OLD_NAME 0x00000004
+ * FILE_ACTION_RENAMED_NEW_NAME 0x00000005
+ * FILE_ACTION_ADDED_STREAM 0x00000006
+ * FILE_ACTION_REMOVED_STREAM 0x00000007
+ * FILE_ACTION_MODIFIED_STREAM 0x00000008
+ */
+
+#include <smbsrv/smb_kproto.h>
+#include <sys/sdt.h>
+
+static void smb_notify_sr(smb_request_t *, uint_t, const char *);
+static uint32_t smb_notify_encode_action(struct smb_request *,
+ mbuf_chain_t *, uint32_t, char *);
+
+uint32_t
+smb_notify_common(smb_request_t *sr, mbuf_chain_t *mbc,
+ uint32_t CompletionFilter)
+{
+ smb_notify_change_req_t *nc;
+ smb_node_t *node;
+ uint32_t status;
+
+ if (sr->fid_ofile == NULL)
+ return (NT_STATUS_INVALID_HANDLE);
+
+ node = sr->fid_ofile->f_node;
+ if (node == NULL || !smb_node_is_dir(node)) {
+ /*
+ * Notify change is only valid on directories.
+ */
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ /*
+ * Prepare to receive event data.
+ */
+ nc = &sr->sr_ncr;
+ nc->nc_flags = CompletionFilter;
+ ASSERT(nc->nc_action == 0);
+ ASSERT(nc->nc_fname == NULL);
+ nc->nc_fname = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
+
+ /*
+ * Subscribe to events on this node.
+ */
+ smb_node_fcn_subscribe(node, sr);
+
+ /*
+ * Wait for subscribed events to arrive.
+ * Expect SMB_REQ_STATE_EVENT_OCCURRED
+ * or SMB_REQ_STATE_CANCELED when signaled.
+ * Note it's possible (though rare) to already
+ * have SMB_REQ_STATE_CANCELED here.
+ */
+ mutex_enter(&sr->sr_mutex);
+ if (sr->sr_state == SMB_REQ_STATE_ACTIVE)
+ sr->sr_state = SMB_REQ_STATE_WAITING_EVENT;
+ while (sr->sr_state == SMB_REQ_STATE_WAITING_EVENT) {
+ cv_wait(&nc->nc_cv, &sr->sr_mutex);
+ }
+ if (sr->sr_state == SMB_REQ_STATE_EVENT_OCCURRED)
+ sr->sr_state = SMB_REQ_STATE_ACTIVE;
+ mutex_exit(&sr->sr_mutex);
+
+ /*
+ * Unsubscribe from events on this node.
+ */
+ smb_node_fcn_unsubscribe(node, sr);
+
+ /*
+ * Why did we wake up?
+ */
+ switch (sr->sr_state) {
+ case SMB_REQ_STATE_ACTIVE:
+ break;
+ case SMB_REQ_STATE_CANCELED:
+ status = NT_STATUS_CANCELLED;
+ goto out;
+ default:
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+
+ /*
+ * We have SMB_REQ_STATE_ACTIVE.
+ *
+ * If we have event data, marshall it now, else just
+ * say "many things changed". Note that when we get
+ * action FILE_ACTION_SUBDIR_CHANGED, we don't have
+ * any event details and only know that some subdir
+ * changed, so just report "many things changed".
+ */
+ switch (nc->nc_action) {
+
+ case FILE_ACTION_ADDED:
+ case FILE_ACTION_REMOVED:
+ case FILE_ACTION_MODIFIED:
+ case FILE_ACTION_RENAMED_OLD_NAME:
+ case FILE_ACTION_RENAMED_NEW_NAME:
+ case FILE_ACTION_ADDED_STREAM:
+ case FILE_ACTION_REMOVED_STREAM:
+ case FILE_ACTION_MODIFIED_STREAM:
+ /*
+ * Build the reply
+ */
+ status = smb_notify_encode_action(sr, mbc,
+ nc->nc_action, nc->nc_fname);
+ break;
+
+ case FILE_ACTION_SUBDIR_CHANGED:
+ status = NT_STATUS_NOTIFY_ENUM_DIR;
+ break;
+
+ case FILE_ACTION_DELETE_PENDING:
+ status = NT_STATUS_DELETE_PENDING;
+ break;
+
+ default:
+ ASSERT(0);
+ status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+out:
+ kmem_free(nc->nc_fname, MAXNAMELEN);
+ nc->nc_fname = NULL;
+ return (status);
+}
+
+/*
+ * Encode a FILE_NOTIFY_INFORMATION struct.
+ *
+ * We only ever put one of these in a response, so this
+ * does not bother handling appending additional ones.
+ */
+static uint32_t
+smb_notify_encode_action(struct smb_request *sr, mbuf_chain_t *mbc,
+ uint32_t action, char *fname)
+{
+ uint32_t namelen;
+
+ ASSERT(FILE_ACTION_ADDED <= action &&
+ action <= FILE_ACTION_MODIFIED_STREAM);
+
+ if (fname == NULL)
+ return (NT_STATUS_INTERNAL_ERROR);
+ namelen = smb_wcequiv_strlen(fname);
+ if (namelen == 0)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ if (smb_mbc_encodef(mbc, "%lllU", sr,
+ 0, /* NextEntryOffset */
+ action, namelen, fname))
+ return (NT_STATUS_NOTIFY_ENUM_DIR);
+
+ return (0);
+}
+
+/*
+ * smb_notify_file_closed
+ *
+ * Cancel any change-notify calls on this open file.
+ */
+void
+smb_notify_file_closed(struct smb_ofile *of)
+{
+ smb_session_t *ses;
+ smb_request_t *sr;
+ smb_slist_t *list;
+
+ SMB_OFILE_VALID(of);
+ ses = of->f_session;
+ SMB_SESSION_VALID(ses);
+ list = &ses->s_req_list;
+
+ smb_slist_enter(list);
+
+ sr = smb_slist_head(list);
+ while (sr) {
+ SMB_REQ_VALID(sr);
+ if (sr->sr_state == SMB_REQ_STATE_WAITING_EVENT &&
+ sr->fid_ofile == of) {
+ smb_request_cancel(sr);
+ }
+ sr = smb_slist_next(list, sr);
+ }
+
+ smb_slist_exit(list);
+}
+
+
+/*
+ * smb_notify_event
+ *
+ * Post an event to the watchers on a given node.
+ *
+ * This makes one exception for RENAME, where we expect a
+ * pair of events for the {old,new} directory element names.
+ * This only delivers an event for the "new" name.
+ *
+ * The event delivery mechanism does not implement delivery of
+ * multiple events for one "NT Notify" call. One could do that,
+ * but modern clients don't actually use the event data. They
+ * set a max. received data size of zero, which means we discard
+ * the data and send the special "lots changed" error instead.
+ * Given that, there's not really any point in implementing the
+ * delivery of multiple events. In fact, we don't even need to
+ * implement single event delivery, but do so for completeness,
+ * for debug convenience, and to be nice to older clients that
+ * may actually want some event data instead of the error.
+ *
+ * Given that we only deliver a single event for an "NT Notify"
+ * caller, we want to deliver the "new" name event. (The "old"
+ * name event is less important, even ignored by some clients.)
+ * Since we know these are delivered in pairs, we can simply
+ * discard the "old" name event, knowing that the "new" name
+ * event will be delivered immediately afterwards.
+ *
+ * So, why do event sources post the "old name" event at all?
+ * (1) For debugging, so we see both {old,new} names here.
+ * (2) If in the future someone decides to implement the
+ * delivery of both {old,new} events, the changes can be
+ * mostly isolated to this file.
+ */
+void
+smb_notify_event(smb_node_t *node, uint_t action, const char *name)
+{
+ smb_request_t *sr;
+ smb_node_fcn_t *fcn;
+
+ SMB_NODE_VALID(node);
+ fcn = &node->n_fcn;
+
+ if (action == FILE_ACTION_RENAMED_OLD_NAME)
+ return; /* see above */
+
+ mutex_enter(&fcn->fcn_mutex);
+
+ sr = list_head(&fcn->fcn_watchers);
+ while (sr) {
+ smb_notify_sr(sr, action, name);
+ sr = list_next(&fcn->fcn_watchers, sr);
+ }
+
+ mutex_exit(&fcn->fcn_mutex);
+}
+
+/*
+ * What completion filter (masks) apply to each of the
+ * FILE_ACTION_... events.
+ */
+static const uint32_t
+smb_notify_action_mask[] = {
+ 0, /* not used */
+
+ /* FILE_ACTION_ADDED */
+ FILE_NOTIFY_CHANGE_NAME |
+ FILE_NOTIFY_CHANGE_LAST_WRITE,
+
+ /* FILE_ACTION_REMOVED */
+ FILE_NOTIFY_CHANGE_NAME |
+ FILE_NOTIFY_CHANGE_LAST_WRITE,
+
+ /* FILE_ACTION_MODIFIED */
+ FILE_NOTIFY_CHANGE_ATTRIBUTES |
+ FILE_NOTIFY_CHANGE_SIZE |
+ FILE_NOTIFY_CHANGE_LAST_WRITE |
+ FILE_NOTIFY_CHANGE_LAST_ACCESS |
+ FILE_NOTIFY_CHANGE_CREATION |
+ FILE_NOTIFY_CHANGE_EA |
+ FILE_NOTIFY_CHANGE_SECURITY,
+
+ /* FILE_ACTION_RENAMED_OLD_NAME */
+ FILE_NOTIFY_CHANGE_NAME |
+ FILE_NOTIFY_CHANGE_LAST_WRITE,
+
+ /* FILE_ACTION_RENAMED_NEW_NAME */
+ FILE_NOTIFY_CHANGE_NAME |
+ FILE_NOTIFY_CHANGE_LAST_WRITE,
+
+ /* FILE_ACTION_ADDED_STREAM */
+ FILE_NOTIFY_CHANGE_STREAM_NAME,
+
+ /* FILE_ACTION_REMOVED_STREAM */
+ FILE_NOTIFY_CHANGE_STREAM_NAME,
+
+ /* FILE_ACTION_MODIFIED_STREAM */
+ FILE_NOTIFY_CHANGE_STREAM_SIZE |
+ FILE_NOTIFY_CHANGE_STREAM_WRITE,
+
+ /* FILE_ACTION_SUBDIR_CHANGED */
+ NODE_FLAGS_WATCH_TREE,
+
+ /* FILE_ACTION_DELETE_PENDING */
+ NODE_FLAGS_WATCH_TREE |
+ FILE_NOTIFY_VALID_MASK,
+};
+static const int smb_notify_action_nelm =
+ sizeof (smb_notify_action_mask) /
+ sizeof (smb_notify_action_mask[0]);
+
+/*
+ * smb_notify_sr
+ *
+ * Post an event to an smb request waiting on some node.
+ *
+ * Note that node->fcn.mutex is held. This implies a
+ * lock order: node->fcn.mutex, then sr_mutex
+ */
+static void
+smb_notify_sr(smb_request_t *sr, uint_t action, const char *name)
+{
+ smb_notify_change_req_t *ncr;
+ uint32_t mask;
+
+ SMB_REQ_VALID(sr);
+ ncr = &sr->sr_ncr;
+
+ /*
+ * Compute the completion filter mask bits for which
+ * we will signal waiting notify requests.
+ */
+ VERIFY(action < smb_notify_action_nelm);
+ mask = smb_notify_action_mask[action];
+
+ mutex_enter(&sr->sr_mutex);
+ if (sr->sr_state == SMB_REQ_STATE_WAITING_EVENT &&
+ (ncr->nc_flags & mask) != 0) {
+ sr->sr_state = SMB_REQ_STATE_EVENT_OCCURRED;
+ /*
+ * Save event data in the sr_ncr field so the
+ * reply handler can return it.
+ */
+ ncr->nc_action = action;
+ if (name != NULL)
+ (void) strlcpy(ncr->nc_fname, name, MAXNAMELEN);
+ cv_signal(&ncr->nc_cv);
+ }
+ mutex_exit(&sr->sr_mutex);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c b/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c
index d9ed7a9234..5b28b0778a 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c
@@ -235,6 +235,7 @@ smb_com_nt_create_andx(struct smb_request *sr)
smb_ofile_t *of;
int rc;
unsigned char DirFlag;
+ uint32_t status;
if ((op->create_options & FILE_DELETE_ON_CLOSE) &&
!(op->desired_access & DELETE)) {
@@ -275,8 +276,11 @@ smb_com_nt_create_andx(struct smb_request *sr)
op->op_oplock_levelII = B_TRUE;
- if (smb_common_open(sr) != NT_STATUS_SUCCESS)
+ status = smb_common_open(sr);
+ if (status != NT_STATUS_SUCCESS) {
+ smbsr_status(sr, status, 0, 0);
return (SDRC_ERROR);
+ }
/*
* NB: after the above smb_common_open() success,
diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c
index 54dd589b7c..7a04ba2b6a 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c
@@ -102,7 +102,7 @@ smb_pre_nt_transact_create(smb_request_t *sr, smb_xa_t *xa)
}
if (sd_len) {
- status = smb_decode_sd(xa, &sd);
+ status = smb_decode_sd(&xa->req_data_mb, &sd);
if (status != NT_STATUS_SUCCESS) {
smbsr_error(sr, status, 0, 0);
return (SDRC_ERROR);
@@ -143,6 +143,7 @@ smb_nt_transact_create(smb_request_t *sr, smb_xa_t *xa)
uint8_t DirFlag;
smb_attr_t attr;
smb_ofile_t *of;
+ uint32_t status;
int rc;
if ((op->create_options & FILE_DELETE_ON_CLOSE) &&
@@ -184,8 +185,11 @@ smb_nt_transact_create(smb_request_t *sr, smb_xa_t *xa)
op->op_oplock_levelII = B_TRUE;
- if (smb_common_open(sr) != NT_STATUS_SUCCESS)
+ status = smb_common_open(sr);
+ if (status != NT_STATUS_SUCCESS) {
+ smbsr_status(sr, status, 0, 0);
return (SDRC_ERROR);
+ }
/*
* NB: after the above smb_common_open() success,
@@ -209,7 +213,7 @@ smb_nt_transact_create(smb_request_t *sr, smb_xa_t *xa)
goto errout;
}
- (void) smb_mbc_encodef(&xa->rep_param_mb, "b.wllTTTTlqqwwb",
+ rc = smb_mbc_encodef(&xa->rep_param_mb, "b.wllTTTTlqqwwb",
op->op_oplock_level,
sr->smb_fid,
op->action_taken,
@@ -228,7 +232,7 @@ smb_nt_transact_create(smb_request_t *sr, smb_xa_t *xa)
case STYPE_IPC:
bzero(&attr, sizeof (smb_attr_t));
- (void) smb_mbc_encodef(&xa->rep_param_mb, "b.wllTTTTlqqwwb",
+ rc = smb_mbc_encodef(&xa->rep_param_mb, "b.wllTTTTlqqwwb",
0,
sr->smb_fid,
op->action_taken,
diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c
index 8fe6d1dde3..c0ab285bd5 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.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 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
@@ -33,6 +33,7 @@ static uint32_t smb_nt_trans_ioctl_set_sparse(smb_request_t *, smb_xa_t *);
static uint32_t smb_nt_trans_ioctl_query_alloc_ranges(smb_request_t *,
smb_xa_t *);
static uint32_t smb_nt_trans_ioctl_set_zero_data(smb_request_t *, smb_xa_t *);
+static uint32_t smb_nt_trans_ioctl_enum_snaps(smb_request_t *, smb_xa_t *);
/*
* This table defines the list of FSCTL values for which we'll
@@ -49,7 +50,7 @@ static const struct {
{ FSCTL_GET_OBJECT_ID, smb_nt_trans_ioctl_invalid_parm },
{ FSCTL_QUERY_ALLOCATED_RANGES, smb_nt_trans_ioctl_query_alloc_ranges },
{ FSCTL_SET_ZERO_DATA, smb_nt_trans_ioctl_set_zero_data },
- { FSCTL_SRV_ENUMERATE_SNAPSHOTS, smb_vss_ioctl_enumerate_snaps },
+ { FSCTL_SRV_ENUMERATE_SNAPSHOTS, smb_nt_trans_ioctl_enum_snaps },
{ FSCTL_SET_SPARSE, smb_nt_trans_ioctl_set_sparse },
{ FSCTL_FIND_FILES_BY_SID, smb_nt_trans_ioctl_noop }
};
@@ -324,3 +325,34 @@ smb_nt_trans_ioctl_query_alloc_ranges(smb_request_t *sr, smb_xa_t *xa)
smbsr_release_file(sr);
return (NT_STATUS_SUCCESS);
}
+
+static uint32_t
+smb_nt_trans_ioctl_enum_snaps(smb_request_t *sr, smb_xa_t *xa)
+{
+ smb_fsctl_t fsctl;
+ uint32_t status;
+
+ if (STYPE_ISIPC(sr->tid_tree->t_res_type))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ smbsr_lookup_file(sr);
+ if (sr->fid_ofile == NULL)
+ return (NT_STATUS_INVALID_HANDLE);
+
+ if (!SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) {
+ smbsr_release_file(sr);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ fsctl.CtlCode = FSCTL_SRV_ENUMERATE_SNAPSHOTS;
+ fsctl.InputCount = xa->smb_tpscnt;
+ fsctl.OutputCount = 0;
+ fsctl.MaxOutputResp = xa->smb_mdrcnt;
+ fsctl.in_mbc = &xa->req_param_mb;
+ fsctl.out_mbc = &xa->rep_data_mb;
+
+ status = smb_vss_enum_snapshots(sr, &fsctl);
+
+ smbsr_release_file(sr);
+ return (status);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c
index 4b6befa344..6ffb021f66 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c
@@ -21,11 +21,12 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
* File Change Notification (FCN)
+ * SMB1 specific part.
*/
/*
@@ -40,43 +41,10 @@
* UCHAR Reserved; MBZ
*
* This command notifies the client when the directory specified by Fid is
- * modified. It also returns the name(s) of the file(s) that changed. The
- * command completes once the directory has been modified based on the
- * supplied CompletionFilter. The command is a "single shot" and therefore
- * needs to be reissued to watch for more directory changes.
+ * modified. See smb_notify.c for details.
*
- * A directory file must be opened before this command may be used. Once
- * the directory is open, this command may be used to begin watching files
- * and subdirectories in the specified directory for changes. The first
- * time the command is issued, the MaxParameterCount field in the transact
- * header determines the size of the buffer that will be used at the server
- * to buffer directory change information between issuances of the notify
- * change commands.
- *
- * When a change that is in the CompletionFilter is made to the directory,
- * the command completes. The names of the files that have changed since
- * the last time the command was issued are returned to the client. The
- * ParameterCount field of the response indicates the number of bytes that
- * are being returned. If too many files have changed since the last time
- * the command was issued, then zero bytes are returned and an alternate
- * status code is returned in the Status field of the response.
- *
- * The CompletionFilter is a mask created as the sum of any of the
- * following flags:
- *
- * FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001
- * FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002
- * FILE_NOTIFY_CHANGE_NAME 0x00000003
- * FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004
- * FILE_NOTIFY_CHANGE_SIZE 0x00000008
- * FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010
- * FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020
- * FILE_NOTIFY_CHANGE_CREATION 0x00000040
- * FILE_NOTIFY_CHANGE_EA 0x00000080
- * FILE_NOTIFY_CHANGE_SECURITY 0x00000100
- * FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200
- * FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400
- * FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800
+ * The MaxParameterCount field in the NT transact header determines
+ * the size of the buffer used to return change information:
*
* Server Response Description
* ================================== ================================
@@ -84,34 +52,10 @@
* Parameters[ ParameterCount ] FILE_NOTIFY_INFORMATION
* structures
*
- * The response contains FILE_NOTIFY_INFORMATION structures, as defined
- * below. The NextEntryOffset field of the structure specifies the offset,
- * in bytes, from the start of the current entry to the next entry in the
- * list. If this is the last entry in the list, this field is zero. Each
- * entry in the list must be longword aligned, so NextEntryOffset must be a
- * multiple of four.
- *
- * typedef struct {
- * ULONG NextEntryOffset;
- * ULONG Action;
- * ULONG FileNameLength;
- * WCHAR FileName[1];
- * } FILE_NOTIFY_INFORMATION;
- *
- * Where Action describes what happened to the file named FileName:
- *
- * FILE_ACTION_ADDED 0x00000001
- * FILE_ACTION_REMOVED 0x00000002
- * FILE_ACTION_MODIFIED 0x00000003
- * FILE_ACTION_RENAMED_OLD_NAME 0x00000004
- * FILE_ACTION_RENAMED_NEW_NAME 0x00000005
- * FILE_ACTION_ADDED_STREAM 0x00000006
- * FILE_ACTION_REMOVED_STREAM 0x00000007
- * FILE_ACTION_MODIFIED_STREAM 0x00000008
+ * See smb_notify.c for details of FILE_NOTIFY_INFORMATION
*/
#include <smbsrv/smb_kproto.h>
-#include <sys/sdt.h>
/*
* We add this flag to the CompletionFilter (see above) when the
@@ -122,11 +66,6 @@
#error "NODE_FLAGS_WATCH_TREE"
#endif
-static void smb_notify_sr(smb_request_t *, uint_t, const char *);
-
-static int smb_notify_encode_action(struct smb_request *, struct smb_xa *,
- uint32_t, char *);
-
/*
* smb_nt_transact_notify_change
*
@@ -145,9 +84,8 @@ smb_nt_transact_notify_change(smb_request_t *sr, struct smb_xa *xa)
{
uint32_t CompletionFilter;
unsigned char WatchTree;
- smb_error_t err;
- int rc;
- smb_node_t *node;
+ uint32_t status;
+ hrtime_t t1, t2;
if (smb_mbc_decodef(&xa->req_setup_mb, "lwb",
&CompletionFilter, &sr->smb_fid, &WatchTree) != 0) {
@@ -159,311 +97,23 @@ smb_nt_transact_notify_change(smb_request_t *sr, struct smb_xa *xa)
CompletionFilter |= NODE_FLAGS_WATCH_TREE;
smbsr_lookup_file(sr);
- if (sr->fid_ofile == NULL) {
- smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
- return (SDRC_ERROR);
- }
-
- node = sr->fid_ofile->f_node;
- if (node == NULL || !smb_node_is_dir(node)) {
- /*
- * Notify change requests are only valid on directories.
- */
- smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 0, 0);
- return (SDRC_ERROR);
- }
-
- /*
- * Prepare to receive event data.
- */
- sr->sr_ncr.nc_flags = CompletionFilter;
- ASSERT(sr->sr_ncr.nc_action == 0);
- ASSERT(sr->sr_ncr.nc_fname == NULL);
- sr->sr_ncr.nc_fname = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
-
- /*
- * Subscribe to events on this node.
- */
- smb_node_fcn_subscribe(node, sr);
- /*
- * Wait for subscribed events to arrive.
- * Expect SMB_REQ_STATE_EVENT_OCCURRED
- * or SMB_REQ_STATE_CANCELED when signaled.
- * Note it's possible (though rare) to already
- * have SMB_REQ_STATE_CANCELED here.
- */
- mutex_enter(&sr->sr_mutex);
- if (sr->sr_state == SMB_REQ_STATE_ACTIVE)
- sr->sr_state = SMB_REQ_STATE_WAITING_EVENT;
- while (sr->sr_state == SMB_REQ_STATE_WAITING_EVENT) {
- cv_wait(&sr->sr_ncr.nc_cv, &sr->sr_mutex);
- }
- if (sr->sr_state == SMB_REQ_STATE_EVENT_OCCURRED)
- sr->sr_state = SMB_REQ_STATE_ACTIVE;
- mutex_exit(&sr->sr_mutex);
+ t1 = gethrtime();
+ status = smb_notify_common(sr, &xa->rep_data_mb, CompletionFilter);
+ t2 = gethrtime();
/*
- * Unsubscribe from events on this node.
+ * We don't want to include the (indefinite) wait time of the
+ * smb_notify_common() call in the SMB1 transact latency.
+ * The easiest way to do that, without adding special case
+ * logic to the common SMB1 dispatch handler is to adjust the
+ * start time of this request to effectively subtract out the
+ * time we were blocked in smb_notify_common().
*/
- smb_node_fcn_unsubscribe(node, sr);
+ sr->sr_time_start += (t2 - t1);
- /*
- * Build the reply
- */
-
- switch (sr->sr_state) {
-
- case SMB_REQ_STATE_ACTIVE:
- /*
- * If we have event data, marshall it now, else just
- * say "many things changed". Note that when we are
- * woken by a WatchTree event (action == 0) then we
- * don't have true event details, and only know the
- * directory under which something changed. In that
- * case we just say "many things changed".
- */
- if (sr->sr_ncr.nc_action != 0 && 0 ==
- smb_notify_encode_action(sr, xa,
- sr->sr_ncr.nc_action, sr->sr_ncr.nc_fname)) {
- rc = SDRC_SUCCESS;
- break;
- }
- /*
- * This error says "many things changed".
- */
- err.status = NT_STATUS_NOTIFY_ENUM_DIR;
- err.errcls = ERRDOS;
- err.errcode = ERROR_NOTIFY_ENUM_DIR;
- smbsr_set_error(sr, &err);
- rc = SDRC_ERROR;
- break;
-
- case SMB_REQ_STATE_CANCELED:
- err.status = NT_STATUS_CANCELLED;
- err.errcls = ERRDOS;
- err.errcode = ERROR_OPERATION_ABORTED;
- smbsr_set_error(sr, &err);
- rc = SDRC_ERROR;
- break;
-
- default:
- ASSERT(0);
- err.status = NT_STATUS_INTERNAL_ERROR;
- err.errcls = ERRDOS;
- err.errcode = ERROR_INTERNAL_ERROR;
- smbsr_set_error(sr, &err);
- rc = SDRC_ERROR;
- break;
- }
-
- if (sr->sr_ncr.nc_fname != NULL) {
- kmem_free(sr->sr_ncr.nc_fname, MAXNAMELEN);
- sr->sr_ncr.nc_fname = NULL;
- }
-
- return (rc);
-}
-
-/*
- * Encode a FILE_NOTIFY_INFORMATION struct.
- *
- * We only ever put one of these in a response, so this
- * does not bother handling appending additional ones.
- */
-static int
-smb_notify_encode_action(struct smb_request *sr, struct smb_xa *xa,
- uint32_t action, char *fname)
-{
- uint32_t namelen;
- int rc;
-
- if (action < FILE_ACTION_ADDED ||
- action > FILE_ACTION_MODIFIED_STREAM)
- return (-1);
-
- namelen = smb_ascii_or_unicode_strlen(sr, fname);
- if (namelen == 0)
- return (-1);
-
- rc = smb_mbc_encodef(&xa->rep_data_mb, "%lllu", sr,
- 0, /* NextEntryOffset */
- action, namelen, fname);
- return (rc);
-}
-
-/*
- * smb_notify_file_closed
- *
- * Cancel any change-notify calls on this open file.
- */
-void
-smb_notify_file_closed(struct smb_ofile *of)
-{
- smb_session_t *ses;
- smb_request_t *sr;
- smb_slist_t *list;
+ if (status != 0)
+ smbsr_error(sr, status, 0, 0);
- SMB_OFILE_VALID(of);
- ses = of->f_session;
- SMB_SESSION_VALID(ses);
- list = &ses->s_req_list;
-
- smb_slist_enter(list);
-
- sr = smb_slist_head(list);
- while (sr) {
- SMB_REQ_VALID(sr);
- if (sr->sr_state == SMB_REQ_STATE_WAITING_EVENT &&
- sr->fid_ofile == of) {
- smb_request_cancel(sr);
- }
- sr = smb_slist_next(list, sr);
- }
-
- smb_slist_exit(list);
-}
-
-
-/*
- * smb_notify_event
- *
- * Post an event to the watchers on a given node.
- *
- * This makes one exception for RENAME, where we expect a
- * pair of events for the {old,new} directory element names.
- * This only delivers an event for the "new" name.
- *
- * The event delivery mechanism does not implement delivery of
- * multiple events for one "NT Notify" call. One could do that,
- * but modern clients don't actually use the event data. They
- * set a max. received data size of zero, which means we discard
- * the data and send the special "lots changed" error instead.
- * Given that, there's not really any point in implementing the
- * delivery of multiple events. In fact, we don't even need to
- * implement single event delivery, but do so for completeness,
- * for debug convenience, and to be nice to older clients that
- * may actually want some event data instead of the error.
- *
- * Given that we only deliver a single event for an "NT Notify"
- * caller, we want to deliver the "new" name event. (The "old"
- * name event is less important, even ignored by some clients.)
- * Since we know these are delivered in pairs, we can simply
- * discard the "old" name event, knowing that the "new" name
- * event will be delivered immediately afterwards.
- *
- * So, why do event sources post the "old name" event at all?
- * (1) For debugging, so we see both {old,new} names here.
- * (2) If in the future someone decides to implement the
- * delivery of both {old,new} events, the changes can be
- * mostly isolated to this file.
- */
-void
-smb_notify_event(smb_node_t *node, uint_t action, const char *name)
-{
- smb_request_t *sr;
- smb_node_fcn_t *fcn;
-
- SMB_NODE_VALID(node);
- fcn = &node->n_fcn;
-
- if (action == FILE_ACTION_RENAMED_OLD_NAME)
- return; /* see above */
-
- mutex_enter(&fcn->fcn_mutex);
-
- sr = list_head(&fcn->fcn_watchers);
- while (sr) {
- smb_notify_sr(sr, action, name);
- sr = list_next(&fcn->fcn_watchers, sr);
- }
-
- mutex_exit(&fcn->fcn_mutex);
-}
-
-/*
- * What completion filter (masks) apply to each of the
- * FILE_ACTION_... events.
- */
-static const uint32_t
-smb_notify_action_mask[] = {
- /* 0: Special, used by smb_node_notify_parents() */
- NODE_FLAGS_WATCH_TREE,
-
- /* FILE_ACTION_ADDED */
- FILE_NOTIFY_CHANGE_NAME,
-
- /* FILE_ACTION_REMOVED */
- FILE_NOTIFY_CHANGE_NAME,
-
- /* FILE_ACTION_MODIFIED */
- FILE_NOTIFY_CHANGE_ATTRIBUTES |
- FILE_NOTIFY_CHANGE_SIZE |
- FILE_NOTIFY_CHANGE_LAST_WRITE |
- FILE_NOTIFY_CHANGE_LAST_ACCESS |
- FILE_NOTIFY_CHANGE_CREATION |
- FILE_NOTIFY_CHANGE_EA |
- FILE_NOTIFY_CHANGE_SECURITY,
-
- /* FILE_ACTION_RENAMED_OLD_NAME */
- FILE_NOTIFY_CHANGE_NAME,
-
- /* FILE_ACTION_RENAMED_NEW_NAME */
- FILE_NOTIFY_CHANGE_NAME,
-
- /* FILE_ACTION_ADDED_STREAM */
- FILE_NOTIFY_CHANGE_STREAM_NAME,
-
- /* FILE_ACTION_REMOVED_STREAM */
- FILE_NOTIFY_CHANGE_STREAM_NAME,
-
- /* FILE_ACTION_MODIFIED_STREAM */
- FILE_NOTIFY_CHANGE_STREAM_SIZE |
- FILE_NOTIFY_CHANGE_STREAM_WRITE,
-};
-static const int smb_notify_action_nelm =
- sizeof (smb_notify_action_mask) /
- sizeof (smb_notify_action_mask[0]);
-
-/*
- * smb_notify_sr
- *
- * Post an event to an smb request waiting on some node.
- *
- * Note that node->fcn.mutex is held. This implies a
- * lock order: node->fcn.mutex, then sr_mutex
- */
-static void
-smb_notify_sr(smb_request_t *sr, uint_t action, const char *name)
-{
- smb_notify_change_req_t *ncr;
- uint32_t mask;
-
- SMB_REQ_VALID(sr);
- ncr = &sr->sr_ncr;
-
- /*
- * Compute the completion filter mask bits for which
- * we will signal waiting notify requests.
- */
- if (action >= smb_notify_action_nelm) {
- ASSERT(0);
- return;
- }
- mask = smb_notify_action_mask[action];
-
- mutex_enter(&sr->sr_mutex);
- if (sr->sr_state == SMB_REQ_STATE_WAITING_EVENT &&
- (ncr->nc_flags & mask) != 0) {
- sr->sr_state = SMB_REQ_STATE_EVENT_OCCURRED;
- /*
- * Save event data in the sr_ncr field so the
- * reply handler can return it.
- */
- ncr->nc_action = action;
- if (name != NULL)
- (void) strlcpy(ncr->nc_fname, name, MAXNAMELEN);
- cv_signal(&ncr->nc_cv);
- }
- mutex_exit(&sr->sr_mutex);
+ return (SDRC_SUCCESS);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_quota.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_quota.c
index 45740dd1bb..c389f38694 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_quota.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_quota.c
@@ -20,47 +20,12 @@
*/
/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
-#include <smbsrv/smb_share.h>
-#include <smbsrv/string.h>
-#include <sys/fs/zfs.h>
-#include <smbsrv/smb_xdr.h>
-#include <smbsrv/smb_door.h>
-#include <smbsrv/smb_idmap.h>
-
-/*
- * A user/group quota entry passed over the wire consists of:
- * - next offset (uint32_t)
- * - length of SID (uint32_t)
- * - last modified time (uint64_t)
- * - quota used (uint64_t)
- * - quota limit (uint64_t)
- * - quota threahold (uint64_t)
- * - variable length sid - max = 32 bytes
- * SMB_QUOTA_SIZE_NO_SID is the size of the above, excluding the sid.
- */
-#define SMB_QUOTA_SIZE_NO_SID \
- ((2 * sizeof (uint32_t)) + (4 * sizeof (uint64_t)))
-#define SMB_QUOTA_EST_SIZE (SMB_QUOTA_SIZE_NO_SID + SMB_EST_SID_SIZE)
-#define SMB_QUOTA_MAX_SIZE (SMB_QUOTA_SIZE_NO_SID + SMB_MAX_SID_SIZE)
-
-static int smb_quota_query(smb_server_t *, smb_quota_query_t *,
- smb_quota_response_t *);
-static int smb_quota_set(smb_server_t *, smb_quota_set_t *, uint32_t *);
-static uint32_t smb_quota_init_sids(smb_xa_t *, smb_quota_query_t *,
- smb_ofile_t *);
-static uint32_t smb_quota_decode_sids(smb_xa_t *, list_t *);
-static void smb_quota_free_sids(smb_quota_query_t *);
-static void smb_quota_max_quota(smb_xa_t *, smb_quota_query_t *);
-static uint32_t smb_quota_decode_quotas(smb_xa_t *, list_t *);
-static uint32_t smb_quota_encode_quotas(smb_xa_t *, smb_quota_query_t *,
- smb_quota_response_t *, smb_ofile_t *);
-static void smb_quota_free_quotas(list_t *);
/*
* smb_nt_transact_query_quota
@@ -183,9 +148,9 @@ smb_nt_transact_query_quota(smb_request_t *sr, smb_xa_t *xa)
request.qq_single = single;
request.qq_restart = restart;
- smb_quota_max_quota(xa, &request);
+ smb_quota_max_quota(&xa->rep_data_mb, &request);
- status = smb_quota_init_sids(xa, &request, ofile);
+ status = smb_quota_init_sids(&xa->req_data_mb, &request, ofile);
if (status == NT_STATUS_SUCCESS) {
if (smb_quota_query(sr->sr_server, &request, &reply) != 0) {
@@ -193,9 +158,12 @@ smb_nt_transact_query_quota(smb_request_t *sr, smb_xa_t *xa)
} else {
status = reply.qr_status;
if (status == NT_STATUS_SUCCESS) {
- status = smb_quota_encode_quotas(xa,
+ status = smb_quota_encode_quotas(
+ &xa->rep_data_mb,
&request, &reply, ofile);
}
+ (void) smb_mbc_encodef(&xa->rep_param_mb, "l",
+ xa->rep_data_mb.chain_offset);
xdr_free(smb_quota_response_xdr, (char *)&reply);
}
}
@@ -311,7 +279,7 @@ smb_nt_transact_set_quota(smb_request_t *sr, smb_xa_t *xa)
list_create(quota_list, sizeof (smb_quota_t),
offsetof(smb_quota_t, q_list_node));
- status = smb_quota_decode_quotas(xa, quota_list);
+ status = smb_quota_decode_quotas(&xa->req_data_mb, quota_list);
if (status == NT_STATUS_SUCCESS) {
request.qs_root_path = root_path;
if (smb_quota_set(sr->sr_server, &request, &reply) != 0) {
@@ -334,411 +302,3 @@ smb_nt_transact_set_quota(smb_request_t *sr, smb_xa_t *xa)
return (SDRC_SUCCESS);
}
-
-/*
- * smb_quota_init_sids
- *
- * If the query is of type SMB_QUOTA_QUERY_SIDLIST or
- * SMB_QUOTA_QUERY_STARTSID decode the list of sids from
- * the client request into request->qq_sid_list.
- * Otherwise (type SMB_QUOTA_QUERY_ALL) find the resume sid
- * and insert it into request->qq_sid_list, or reset the
- * resume sid to NULL if request->qq_restart.
- *
- * Returns: NT_STATUS codes
- */
-static uint32_t
-smb_quota_init_sids(smb_xa_t *xa, smb_quota_query_t *request,
- smb_ofile_t *ofile)
-{
- smb_quota_sid_t *sid;
- list_t *sid_list;
- uint32_t status = NT_STATUS_SUCCESS;
-
- sid_list = &request->qq_sid_list;
- list_create(sid_list, sizeof (smb_quota_sid_t),
- offsetof(smb_quota_sid_t, qs_list_node));
-
- switch (request->qq_query_op) {
- case SMB_QUOTA_QUERY_SIDLIST:
- case SMB_QUOTA_QUERY_STARTSID:
- status = smb_quota_decode_sids(xa, sid_list);
- break;
- case SMB_QUOTA_QUERY_ALL:
- if (request->qq_restart)
- smb_ofile_set_quota_resume(ofile, NULL);
- else {
- sid = kmem_zalloc(sizeof (smb_quota_sid_t), KM_SLEEP);
- list_insert_tail(sid_list, sid);
- smb_ofile_get_quota_resume(ofile, sid->qs_sidstr,
- SMB_SID_STRSZ);
- if (*sid->qs_sidstr == '\0')
- status = NT_STATUS_INVALID_PARAMETER;
- }
- break;
- default:
- status = NT_STATUS_INVALID_PARAMETER;
- break;
- }
-
- return (status);
-}
-
-/*
- * smb_quota_free_sids
- */
-static void
-smb_quota_free_sids(smb_quota_query_t *request)
-{
- list_t *sid_list;
- smb_quota_sid_t *sid;
-
- sid_list = &request->qq_sid_list;
-
- while ((sid = list_head(sid_list)) != NULL) {
- list_remove(sid_list, sid);
- kmem_free(sid, sizeof (smb_quota_sid_t));
- }
-
- list_destroy(sid_list);
-}
-
-/*
- * smb_quota_decode_sids
- *
- * Decode the SIDs from the data block and stores them in string form in list.
- * Eaxh sid entry comprises:
- * next_offset (4 bytes) - offset of next entry
- * sid length (4 bytes)
- * sid (variable length = sidlen)
- * The last entry will have a next_offset value of 0.
- *
- * Returns NT_STATUS codes.
- */
-static uint32_t
-smb_quota_decode_sids(smb_xa_t *xa, list_t *list)
-{
- uint32_t offset, mb_offset, sid_offset, bytes_left;
- uint32_t next_offset, sidlen;
- smb_sid_t *sid;
- smb_quota_sid_t *qsid;
- uint32_t status = NT_STATUS_SUCCESS;
- struct mbuf_chain sidbuf;
-
- offset = 0;
- do {
- mb_offset = offset + xa->req_data_mb.chain_offset;
- bytes_left = xa->req_data_mb.max_bytes - mb_offset;
- (void) MBC_SHADOW_CHAIN(&sidbuf, &xa->req_data_mb,
- mb_offset, bytes_left);
-
- if (smb_mbc_decodef(&sidbuf, "ll", &next_offset, &sidlen)) {
- status = NT_STATUS_INVALID_PARAMETER;
- break;
- }
-
- sid_offset = offset + (2 * sizeof (uint32_t));
- sid = smb_decode_sid(xa, sid_offset);
- if (sid == NULL) {
- status = NT_STATUS_INVALID_PARAMETER;
- break;
- }
-
- qsid = kmem_zalloc(sizeof (smb_quota_sid_t), KM_SLEEP);
- smb_sid_tostr(sid, qsid->qs_sidstr);
- smb_sid_free(sid);
- sid = NULL;
-
- list_insert_tail(list, qsid);
- offset += next_offset;
- } while ((next_offset != 0) && (bytes_left > 0));
-
- return (status);
-}
-
-/*
- * smb_quota_max_quota
- *
- * If the query is if type SMB_QUOTA_QUERY_SIDLIST a quota entry
- * is returned for each sid in the sidlist. request->qr_max_quota
- * is set to 0 and is unused.
- * Otherwise (for SMB_QUOTA_QUERY_STARTSID and SMB_QUOTA_QUERY_ALL)
- * max_quota is the maximum number of quota entries requested from
- * the file system (via door call smb_quota_query()).
- * If single is set max_quota is set to 1. If single is not set
- * max quota is calculated as the number of quotas of size
- * SMB_QUOTA_EST_SIZE that would fit in the response buffer.
- */
-static void
-smb_quota_max_quota(smb_xa_t *xa, smb_quota_query_t *request)
-{
- if (request->qq_query_op == SMB_QUOTA_QUERY_SIDLIST)
- request->qq_max_quota = 0;
- else if (request->qq_single)
- request->qq_max_quota = 1;
- else
- request->qq_max_quota = (xa->smb_mdrcnt / SMB_QUOTA_EST_SIZE);
-}
-
-/*
- * smb_quota_decode_quotas
- *
- * Decode the quota entries into a list_t of smb_quota_t.
- * SMB_QUOTA_SIZE_NO_SID is the size of a quota entry,
- * excluding the sid.
- * The last entry will have a next_offset value of 0.
- *
- * Returns NT_STATUS codes.
- */
-static uint32_t
-smb_quota_decode_quotas(smb_xa_t *xa, list_t *list)
-{
- uint32_t offset, mb_offset, sid_offset, bytes_left;
- uint32_t next_offset, sidlen;
- uint64_t mtime;
- smb_sid_t *sid;
- smb_quota_t *quota;
- uint32_t status = NT_STATUS_SUCCESS;
- struct mbuf_chain quotabuf;
-
- offset = 0;
- do {
- mb_offset = offset + xa->req_data_mb.chain_offset;
- bytes_left = xa->req_data_mb.max_bytes - mb_offset;
- (void) MBC_SHADOW_CHAIN(&quotabuf, &xa->req_data_mb,
- mb_offset, bytes_left);
-
- quota = kmem_zalloc(sizeof (smb_quota_t), KM_SLEEP);
-
- if (smb_mbc_decodef(&quotabuf, "llqqqq",
- &next_offset, &sidlen, &mtime,
- &quota->q_used, &quota->q_thresh, &quota->q_limit)) {
- kmem_free(quota, sizeof (smb_quota_t));
- status = NT_STATUS_INVALID_PARAMETER;
- break;
- }
-
- sid_offset = offset + SMB_QUOTA_SIZE_NO_SID;
- sid = smb_decode_sid(xa, sid_offset);
- if (sid == NULL) {
- kmem_free(quota, sizeof (smb_quota_t));
- status = NT_STATUS_INVALID_PARAMETER;
- break;
- }
-
- bzero(quota->q_sidstr, SMB_SID_STRSZ);
- smb_sid_tostr(sid, quota->q_sidstr);
- smb_sid_free(sid);
- sid = NULL;
-
- list_insert_tail(list, quota);
- offset += next_offset;
- } while ((next_offset != 0) && (bytes_left > 0));
-
- return (status);
-}
-
-/*
- * smb_quota_free_quotas
- */
-static void
-smb_quota_free_quotas(list_t *list)
-{
- smb_quota_t *quota;
-
- while ((quota = list_head(list)) != NULL) {
- list_remove(list, quota);
- kmem_free(quota, sizeof (smb_quota_t));
- }
-
- list_destroy(list);
-}
-
-/*
- * smb_quota_encode_quotas
- *
- * Encode the quota entries from a list_t of smb_quota_t.
- * SMB_QUOTA_SIZE_NO_SID is the size of a quota entry,
- * excluding the sid.
- * The last entry will have a next_offset value of 0.
- * Sets the last encoded SID as the resume sid.
- */
-static uint32_t
-smb_quota_encode_quotas(smb_xa_t *xa, smb_quota_query_t *request,
- smb_quota_response_t *reply, smb_ofile_t *ofile)
-{
- uint32_t next_offset, sid_offset, total_bytes;
- uint64_t mtime = 0;
- uint32_t sidlen, pad;
- smb_sid_t *sid;
- char *sidstr = NULL, *resume = NULL;
- smb_quota_t *quota, *next_quota;
- list_t *list = &reply->qr_quota_list;
-
- int rc;
- uint32_t status = NT_STATUS_SUCCESS;
-
- total_bytes = 0;
- quota = list_head(list);
- while (quota) {
- next_quota = list_next(list, quota);
- sidstr = quota->q_sidstr;
- if ((sid = smb_sid_fromstr(sidstr)) == NULL) {
- quota = next_quota;
- continue;
- }
-
- sidlen = smb_sid_len(sid);
- sid_offset = SMB_QUOTA_SIZE_NO_SID;
- next_offset = sid_offset + sidlen;
- pad = smb_pad_align(next_offset, 8);
- next_offset += pad;
-
- if (!MBC_ROOM_FOR(&xa->rep_data_mb, next_offset)) {
- smb_sid_free(sid);
- break;
- }
- if (!MBC_ROOM_FOR(&xa->rep_data_mb,
- next_offset + SMB_QUOTA_MAX_SIZE)) {
- next_quota = NULL;
- }
-
- rc = smb_mbc_encodef(&xa->rep_data_mb, "llqqqq",
- next_quota ? next_offset : 0, sidlen, mtime,
- quota->q_used, quota->q_thresh, quota->q_limit);
- if (rc == 0) {
- smb_encode_sid(xa, sid);
- rc = smb_mbc_encodef(&xa->rep_data_mb, "#.", pad);
- }
-
- smb_sid_free(sid);
-
- if (rc != 0) {
- status = NT_STATUS_INTERNAL_ERROR;
- break;
- }
-
- resume = sidstr;
- total_bytes += next_offset;
- quota = next_quota;
- }
-
- rc = smb_mbc_encodef(&xa->rep_param_mb, "l", total_bytes);
-
- if ((status == NT_STATUS_SUCCESS) &&
- ((request->qq_query_op == SMB_QUOTA_QUERY_STARTSID) ||
- (request->qq_query_op == SMB_QUOTA_QUERY_ALL))) {
- smb_ofile_set_quota_resume(ofile, resume);
- }
-
- return (status);
-}
-
-/*
- * smb_quota_query_user_quota
- *
- * Get user quota information for a single user (uid)
- * for the current file system.
- * Find the user's sid, insert it in the sidlist of a
- * smb_quota_query_t request and invoke the door call
- * smb_quota_query() to obtain the quota information.
- *
- * Returns: NT_STATUS codes.
- */
-uint32_t
-smb_quota_query_user_quota(smb_request_t *sr, uid_t uid, smb_quota_t *quota)
-{
- smb_sid_t *sid;
- smb_quota_sid_t qsid;
- smb_quota_query_t request;
- smb_quota_response_t reply;
- list_t *sid_list;
- smb_quota_t *q;
- smb_node_t *tnode;
- uint32_t status = NT_STATUS_SUCCESS;
-
- if (smb_idmap_getsid(uid, SMB_IDMAP_USER, &sid) != IDMAP_SUCCESS)
- return (NT_STATUS_INTERNAL_ERROR);
-
- smb_sid_tostr(sid, qsid.qs_sidstr);
- smb_sid_free(sid);
-
- bzero(&request, sizeof (smb_quota_query_t));
- bzero(&reply, sizeof (smb_quota_response_t));
-
- tnode = sr->tid_tree->t_snode;
- request.qq_root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
- if (smb_node_getmntpath(tnode, request.qq_root_path, MAXPATHLEN) != 0) {
- kmem_free(request.qq_root_path, MAXPATHLEN);
- return (NT_STATUS_INTERNAL_ERROR);
- }
-
- sid_list = &request.qq_sid_list;
- list_create(sid_list, sizeof (smb_quota_sid_t),
- offsetof(smb_quota_sid_t, qs_list_node));
- list_insert_tail(sid_list, &qsid);
-
- request.qq_query_op = SMB_QUOTA_QUERY_SIDLIST;
- request.qq_single = B_TRUE;
-
- if (smb_quota_query(sr->sr_server, &request, &reply) != 0) {
- status = NT_STATUS_INTERNAL_ERROR;
- } else {
- if (reply.qr_status != NT_STATUS_SUCCESS) {
- status = reply.qr_status;
- } else {
- q = list_head(&reply.qr_quota_list);
- if ((q == NULL) ||
- (strcmp(qsid.qs_sidstr, q->q_sidstr) != 0)) {
- /* should never happen */
- status = NT_STATUS_INTERNAL_ERROR;
- } else {
- bcopy(q, quota, sizeof (smb_quota_t));
- }
- }
- xdr_free(smb_quota_response_xdr, (char *)&reply);
- }
-
- kmem_free(request.qq_root_path, MAXPATHLEN);
- list_remove(sid_list, &qsid);
- list_destroy(sid_list);
-
- return (status);
-}
-
-/*
- * smb_quota_query
- *
- * Door call to query quotas for the provided filesystem path.
- * Returns: -1 - door call (or encode/decode) failure.
- * 0 - success. Status set in reply.
- */
-static int
-smb_quota_query(smb_server_t *sv, smb_quota_query_t *request,
- smb_quota_response_t *reply)
-{
- int rc;
-
- rc = smb_kdoor_upcall(sv, SMB_DR_QUOTA_QUERY,
- request, smb_quota_query_xdr, reply, smb_quota_response_xdr);
-
- return (rc);
-}
-
-/*
- * smb_quota_set
- *
- * Door call to set quotas for the provided filesystem path.
- * Returns: -1 - door call (or encode/decode) failure.
- * 0 - success. Status set in reply.
- */
-static int
-smb_quota_set(smb_server_t *sv, smb_quota_set_t *request, uint32_t *reply)
-{
- int rc;
-
- rc = smb_kdoor_upcall(sv, SMB_DR_QUOTA_SET,
- request, smb_quota_set_xdr, reply, xdr_uint32_t);
-
- return (rc);
-}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_security.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_security.c
index cb24963d29..98056d1749 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_security.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_security.c
@@ -21,14 +21,14 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
-static void smb_encode_sd(struct smb_xa *, smb_sd_t *, uint32_t);
-static void smb_encode_sacl(struct smb_xa *, smb_acl_t *);
-static void smb_encode_dacl(struct smb_xa *, smb_acl_t *);
-static smb_acl_t *smb_decode_acl(struct smb_xa *, uint32_t);
+static void smb_encode_sacl(mbuf_chain_t *, smb_acl_t *);
+static void smb_encode_dacl(mbuf_chain_t *, smb_acl_t *);
+static smb_acl_t *smb_decode_acl(mbuf_chain_t *, uint32_t);
/*
* smb_nt_transact_query_security_info
@@ -124,7 +124,7 @@ smb_nt_transact_query_security_info(struct smb_request *sr, struct smb_xa *xa)
return (SDRC_SUCCESS);
}
- smb_encode_sd(xa, &sd, secinfo);
+ smb_encode_sd(&xa->rep_data_mb, &sd, secinfo);
(void) smb_mbc_encodef(&xa->rep_param_mb, "l", sdlen);
smb_sd_term(&sd);
return (SDRC_SUCCESS);
@@ -193,7 +193,7 @@ smb_nt_transact_set_security_info(struct smb_request *sr, struct smb_xa *xa)
return (NT_STATUS_SUCCESS);
}
- status = smb_decode_sd(xa, &sd);
+ status = smb_decode_sd(&xa->req_data_mb, &sd);
if (status != NT_STATUS_SUCCESS) {
smbsr_error(sr, status, 0, 0);
return (SDRC_ERROR);
@@ -222,58 +222,58 @@ smb_nt_transact_set_security_info(struct smb_request *sr, struct smb_xa *xa)
*
* Encodes given security descriptor in the reply buffer.
*/
-static void
-smb_encode_sd(struct smb_xa *xa, smb_sd_t *sd, uint32_t secinfo)
+void
+smb_encode_sd(mbuf_chain_t *mbc, smb_sd_t *sd, uint32_t secinfo)
{
uint32_t offset = SMB_SD_HDRSIZE;
/* encode header */
- (void) smb_mbc_encodef(&xa->rep_data_mb, "b.w",
+ (void) smb_mbc_encodef(mbc, "b.w",
sd->sd_revision, sd->sd_control | SE_SELF_RELATIVE);
/* owner offset */
if (secinfo & SMB_OWNER_SECINFO) {
ASSERT(sd->sd_owner);
- (void) smb_mbc_encodef(&xa->rep_data_mb, "l", offset);
+ (void) smb_mbc_encodef(mbc, "l", offset);
offset += smb_sid_len(sd->sd_owner);
} else {
- (void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0);
+ (void) smb_mbc_encodef(mbc, "l", 0);
}
/* group offset */
if (secinfo & SMB_GROUP_SECINFO) {
ASSERT(sd->sd_group);
- (void) smb_mbc_encodef(&xa->rep_data_mb, "l", offset);
+ (void) smb_mbc_encodef(mbc, "l", offset);
offset += smb_sid_len(sd->sd_group);
} else {
- (void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0);
+ (void) smb_mbc_encodef(mbc, "l", 0);
}
/* SACL offset */
if ((secinfo & SMB_SACL_SECINFO) && (sd->sd_sacl)) {
- (void) smb_mbc_encodef(&xa->rep_data_mb, "l", offset);
+ (void) smb_mbc_encodef(mbc, "l", offset);
offset += smb_acl_len(sd->sd_sacl);
} else {
- (void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0);
+ (void) smb_mbc_encodef(mbc, "l", 0);
}
/* DACL offset */
if ((secinfo & SMB_DACL_SECINFO) && (sd->sd_dacl))
- (void) smb_mbc_encodef(&xa->rep_data_mb, "l", offset);
+ (void) smb_mbc_encodef(mbc, "l", offset);
else
- (void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0);
+ (void) smb_mbc_encodef(mbc, "l", 0);
if (secinfo & SMB_OWNER_SECINFO)
- smb_encode_sid(xa, sd->sd_owner);
+ smb_encode_sid(mbc, sd->sd_owner);
if (secinfo & SMB_GROUP_SECINFO)
- smb_encode_sid(xa, sd->sd_group);
+ smb_encode_sid(mbc, sd->sd_group);
if (secinfo & SMB_SACL_SECINFO)
- smb_encode_sacl(xa, sd->sd_sacl);
+ smb_encode_sacl(mbc, sd->sd_sacl);
if (secinfo & SMB_DACL_SECINFO)
- smb_encode_dacl(xa, sd->sd_dacl);
+ smb_encode_dacl(mbc, sd->sd_dacl);
}
/*
@@ -282,20 +282,20 @@ smb_encode_sd(struct smb_xa *xa, smb_sd_t *sd, uint32_t secinfo)
* Encodes given SID in the reply buffer.
*/
void
-smb_encode_sid(struct smb_xa *xa, smb_sid_t *sid)
+smb_encode_sid(mbuf_chain_t *mbc, smb_sid_t *sid)
{
int i;
- (void) smb_mbc_encodef(&xa->rep_data_mb, "bb",
+ (void) smb_mbc_encodef(mbc, "bb",
sid->sid_revision, sid->sid_subauthcnt);
for (i = 0; i < NT_SID_AUTH_MAX; i++) {
- (void) smb_mbc_encodef(&xa->rep_data_mb, "b",
+ (void) smb_mbc_encodef(mbc, "b",
sid->sid_authority[i]);
}
for (i = 0; i < sid->sid_subauthcnt; i++) {
- (void) smb_mbc_encodef(&xa->rep_data_mb, "l",
+ (void) smb_mbc_encodef(mbc, "l",
sid->sid_subauth[i]);
}
}
@@ -306,7 +306,7 @@ smb_encode_sid(struct smb_xa *xa, smb_sid_t *sid)
* Encodes given SACL in the reply buffer.
*/
static void
-smb_encode_sacl(struct smb_xa *xa, smb_acl_t *acl)
+smb_encode_sacl(mbuf_chain_t *mbc, smb_acl_t *acl)
{
smb_ace_t *ace;
int i;
@@ -315,15 +315,15 @@ smb_encode_sacl(struct smb_xa *xa, smb_acl_t *acl)
return;
/* encode header */
- (void) smb_mbc_encodef(&xa->rep_data_mb, "b.ww2.", acl->sl_revision,
+ (void) smb_mbc_encodef(mbc, "b.ww2.", acl->sl_revision,
acl->sl_bsize, acl->sl_acecnt);
for (i = 0, ace = acl->sl_aces; i < acl->sl_acecnt; i++, ace++) {
- (void) smb_mbc_encodef(&xa->rep_data_mb, "bbwl",
+ (void) smb_mbc_encodef(mbc, "bbwl",
ace->se_hdr.se_type, ace->se_hdr.se_flags,
ace->se_hdr.se_bsize, ace->se_mask);
- smb_encode_sid(xa, ace->se_sid);
+ smb_encode_sid(mbc, ace->se_sid);
}
}
@@ -333,7 +333,7 @@ smb_encode_sacl(struct smb_xa *xa, smb_acl_t *acl)
* Encodes given DACL in the reply buffer.
*/
static void
-smb_encode_dacl(struct smb_xa *xa, smb_acl_t *acl)
+smb_encode_dacl(mbuf_chain_t *mbc, smb_acl_t *acl)
{
smb_ace_t *ace;
@@ -341,16 +341,16 @@ smb_encode_dacl(struct smb_xa *xa, smb_acl_t *acl)
return;
/* encode header */
- (void) smb_mbc_encodef(&xa->rep_data_mb, "b.ww2.", acl->sl_revision,
+ (void) smb_mbc_encodef(mbc, "b.ww2.", acl->sl_revision,
acl->sl_bsize, acl->sl_acecnt);
ace = list_head(&acl->sl_sorted);
while (ace) {
- (void) smb_mbc_encodef(&xa->rep_data_mb, "bbwl",
+ (void) smb_mbc_encodef(mbc, "bbwl",
ace->se_hdr.se_type, ace->se_hdr.se_flags,
ace->se_hdr.se_bsize, ace->se_mask);
- smb_encode_sid(xa, ace->se_sid);
+ smb_encode_sid(mbc, ace->se_sid);
ace = list_next(&acl->sl_sorted, ace);
}
}
@@ -364,7 +364,7 @@ smb_encode_dacl(struct smb_xa *xa, smb_acl_t *acl)
* smb_sd_term().
*/
uint32_t
-smb_decode_sd(struct smb_xa *xa, smb_sd_t *sd)
+smb_decode_sd(mbuf_chain_t *mbc, smb_sd_t *sd)
{
struct mbuf_chain sdbuf;
uint32_t owner_offs;
@@ -374,9 +374,9 @@ smb_decode_sd(struct smb_xa *xa, smb_sd_t *sd)
smb_sd_init(sd, SECURITY_DESCRIPTOR_REVISION);
- (void) MBC_SHADOW_CHAIN(&sdbuf, &xa->req_data_mb,
- xa->req_data_mb.chain_offset,
- xa->req_data_mb.max_bytes - xa->req_data_mb.chain_offset);
+ (void) MBC_SHADOW_CHAIN(&sdbuf, mbc,
+ mbc->chain_offset,
+ mbc->max_bytes - mbc->chain_offset);
if (smb_mbc_decodef(&sdbuf, "b.wllll",
&sd->sd_revision, &sd->sd_control,
@@ -389,7 +389,7 @@ smb_decode_sd(struct smb_xa *xa, smb_sd_t *sd)
if (owner_offs < SMB_SD_HDRSIZE)
goto decode_error;
- sd->sd_owner = smb_decode_sid(xa, owner_offs);
+ sd->sd_owner = smb_decode_sid(mbc, owner_offs);
if (sd->sd_owner == NULL)
goto decode_error;
}
@@ -398,7 +398,7 @@ smb_decode_sd(struct smb_xa *xa, smb_sd_t *sd)
if (group_offs < SMB_SD_HDRSIZE)
goto decode_error;
- sd->sd_group = smb_decode_sid(xa, group_offs);
+ sd->sd_group = smb_decode_sid(mbc, group_offs);
if (sd->sd_group == NULL)
goto decode_error;
}
@@ -410,7 +410,7 @@ smb_decode_sd(struct smb_xa *xa, smb_sd_t *sd)
if (sacl_offs < SMB_SD_HDRSIZE)
goto decode_error;
- sd->sd_sacl = smb_decode_acl(xa, sacl_offs);
+ sd->sd_sacl = smb_decode_acl(mbc, sacl_offs);
if (sd->sd_sacl == NULL)
goto decode_error;
}
@@ -422,7 +422,7 @@ smb_decode_sd(struct smb_xa *xa, smb_sd_t *sd)
if (dacl_offs < SMB_SD_HDRSIZE)
goto decode_error;
- sd->sd_dacl = smb_decode_acl(xa, dacl_offs);
+ sd->sd_dacl = smb_decode_acl(mbc, dacl_offs);
if (sd->sd_dacl == NULL)
goto decode_error;
}
@@ -442,7 +442,7 @@ decode_error:
* by calling smb_sid_free()
*/
smb_sid_t *
-smb_decode_sid(struct smb_xa *xa, uint32_t offset)
+smb_decode_sid(mbuf_chain_t *mbc, uint32_t offset)
{
uint8_t revision;
uint8_t subauth_cnt;
@@ -452,12 +452,13 @@ smb_decode_sid(struct smb_xa *xa, uint32_t offset)
int bytes_left;
int i;
- offset += xa->req_data_mb.chain_offset;
- bytes_left = xa->req_data_mb.max_bytes - offset;
- if (bytes_left < sizeof (smb_sid_t))
+ offset += mbc->chain_offset;
+ bytes_left = mbc->max_bytes - offset;
+ if (bytes_left < (int)sizeof (smb_sid_t))
return (NULL);
- (void) MBC_SHADOW_CHAIN(&sidbuf, &xa->req_data_mb, offset, bytes_left);
+ if (MBC_SHADOW_CHAIN(&sidbuf, mbc, offset, bytes_left) != 0)
+ return (NULL);
if (smb_mbc_decodef(&sidbuf, "bb", &revision, &subauth_cnt))
return (NULL);
@@ -494,7 +495,7 @@ decode_err:
* by calling smb_acl_free().
*/
static smb_acl_t *
-smb_decode_acl(struct smb_xa *xa, uint32_t offset)
+smb_decode_acl(mbuf_chain_t *mbc, uint32_t offset)
{
struct mbuf_chain aclbuf;
smb_acl_t *acl;
@@ -507,12 +508,13 @@ smb_decode_acl(struct smb_xa *xa, uint32_t offset)
int sidlen;
int i;
- offset += xa->req_data_mb.chain_offset;
- bytes_left = xa->req_data_mb.max_bytes - offset;
+ offset += mbc->chain_offset;
+ bytes_left = mbc->max_bytes - offset;
if (bytes_left < SMB_ACL_HDRSIZE)
return (NULL);
- (void) MBC_SHADOW_CHAIN(&aclbuf, &xa->req_data_mb, offset, bytes_left);
+ if (MBC_SHADOW_CHAIN(&aclbuf, mbc, offset, bytes_left) != 0)
+ return (NULL);
if (smb_mbc_decodef(&aclbuf, "b.ww2.", &revision, &size, &acecnt))
return (NULL);
@@ -530,7 +532,7 @@ smb_decode_acl(struct smb_xa *xa, uint32_t offset)
goto decode_error;
sid_offs += SMB_ACE_HDRSIZE + sizeof (ace->se_mask);
- ace->se_sid = smb_decode_sid(xa, sid_offs);
+ ace->se_sid = smb_decode_sid(mbc, sid_offs);
if (ace->se_sid == NULL)
goto decode_error;
/* This is SID length plus any paddings between ACEs */
diff --git a/usr/src/uts/common/fs/smbsrv/smb_odir.c b/usr/src/uts/common/fs/smbsrv/smb_odir.c
index 279a2cc7ef..54fa075dbe 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_odir.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_odir.c
@@ -142,16 +142,21 @@
*
* Odir Interface
* ---------------
- * odid = smb_odir_open(pathname)
+ * smb_odir_open(char *pathname)
* Create an odir representing the directory specified in pathname and
* add it into the tree's list of odirs.
- * Return an identifier (odid) uniquely identifying the created odir.
+ * Returns NT status.
+ *
+ * smb_odir_openfh(smb_ofile_t *of)
+ * Create an odir representing the directory specified by the
+ * existing open handle (from a prior open of the directory).
+ * Returns NT status.
*
* smb_odir_openat(smb_node_t *unode)
* Create an odir representing the extended attribute directory
* associated with the file (or directory) represented by unode
* and add it into the tree's list of odirs.
- * Return an identifier (odid) uniquely identifying the created odir.
+ * Returns NT status.
*
* smb_odir_t *odir = smb_tree_lookup_odir(..., odid)
* Find the odir corresponding to the specified odid in the tree's
@@ -249,8 +254,8 @@
#include <sys/extdirent.h>
/* static functions */
-static uint16_t smb_odir_create(smb_request_t *, smb_node_t *,
- char *, uint16_t, cred_t *);
+static smb_odir_t *smb_odir_create(smb_request_t *, smb_node_t *,
+ const char *, uint16_t, uint16_t, cred_t *);
static int smb_odir_single_fileinfo(smb_request_t *, smb_odir_t *,
smb_fileinfo_t *);
static int smb_odir_wildcard_fileinfo(smb_request_t *, smb_odir_t *,
@@ -262,16 +267,16 @@ static boolean_t smb_odir_match_name(smb_odir_t *, smb_odirent_t *);
/*
- * smb_odir_open
+ * smb_odir_openpath
*
* Create an odir representing the directory specified in pathname.
*
* Returns:
- * odid - Unique identifier of newly created odir.
- * 0 - error, error details set in sr.
+ * NT Status
*/
-uint16_t
-smb_odir_open(smb_request_t *sr, char *path, uint16_t sattr, uint32_t flags)
+uint32_t
+smb_odir_openpath(smb_request_t *sr, char *path, uint16_t sattr,
+ uint32_t flags, smb_odir_t **odp)
{
int rc;
smb_tree_t *tree;
@@ -284,6 +289,7 @@ smb_odir_open(smb_request_t *sr, char *path, uint16_t sattr, uint32_t flags)
ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
ASSERT(sr->tid_tree);
ASSERT(sr->tid_tree->t_magic == SMB_TREE_MAGIC);
+ *odp = NULL;
tree = sr->tid_tree;
@@ -292,23 +298,22 @@ smb_odir_open(smb_request_t *sr, char *path, uint16_t sattr, uint32_t flags)
rc = smb_pathname_reduce(sr, sr->user_cr, path,
tree->t_snode, tree->t_snode, &dnode, pattern);
- if (rc != 0) {
- smbsr_errno(sr, rc);
- return (0);
- }
+ if (rc != 0)
+ return (smb_errno2status(rc));
if (!smb_node_is_dir(dnode)) {
- smbsr_error(sr, NT_STATUS_OBJECT_PATH_NOT_FOUND,
- ERRDOS, ERROR_PATH_NOT_FOUND);
smb_node_release(dnode);
- return (0);
+ return (NT_STATUS_OBJECT_PATH_NOT_FOUND);
}
if (smb_fsop_access(sr, sr->user_cr, dnode, FILE_LIST_DIRECTORY) != 0) {
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
- ERRDOS, ERROR_ACCESS_DENIED);
smb_node_release(dnode);
- return (0);
+ return (NT_STATUS_ACCESS_DENIED);
+ }
+
+ if (smb_idpool_alloc(&tree->t_odid_pool, &odid)) {
+ smb_node_release(dnode);
+ return (NT_STATUS_TOO_MANY_OPENED_FILES);
}
if (flags & SMB_ODIR_OPENF_BACKUP_INTENT)
@@ -316,9 +321,37 @@ smb_odir_open(smb_request_t *sr, char *path, uint16_t sattr, uint32_t flags)
else
cr = sr->uid_user->u_cred;
- odid = smb_odir_create(sr, dnode, pattern, sattr, cr);
+ *odp = smb_odir_create(sr, dnode, pattern, sattr, odid, cr);
smb_node_release(dnode);
- return (odid);
+
+ return (0);
+}
+
+/*
+ * smb_odir_openfh
+ *
+ * Create an odir representing the directory already opened on "of".
+ *
+ * Returns:
+ * NT status
+ */
+uint32_t
+smb_odir_openfh(smb_request_t *sr, const char *pattern, uint16_t sattr,
+ smb_odir_t **odp)
+{
+ smb_ofile_t *of = sr->fid_ofile;
+
+ *odp = NULL;
+
+ if (of->f_node == NULL || !smb_node_is_dir(of->f_node))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ if ((of->f_granted_access & FILE_LIST_DIRECTORY) == 0)
+ return (NT_STATUS_ACCESS_DENIED);
+
+ *odp = smb_odir_create(sr, of->f_node, pattern, sattr, 0, of->f_cr);
+
+ return (0);
}
/*
@@ -328,55 +361,47 @@ smb_odir_open(smb_request_t *sr, char *path, uint16_t sattr, uint32_t flags)
* associated with the file (or directory) represented by unode.
*
* Returns:
- * odid - Unique identifier of newly created odir.
- * 0 - error, error details set in sr.
+ * NT status
*/
-uint16_t
-smb_odir_openat(smb_request_t *sr, smb_node_t *unode)
+uint32_t
+smb_odir_openat(smb_request_t *sr, smb_node_t *unode, smb_odir_t **odp)
{
- int rc;
+ char pattern[SMB_STREAM_PREFIX_LEN + 2];
vnode_t *xattr_dvp;
- uint16_t odid;
cred_t *cr;
- char pattern[SMB_STREAM_PREFIX_LEN + 2];
-
smb_node_t *xattr_dnode;
+ int rc;
ASSERT(sr);
ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
ASSERT(unode);
ASSERT(unode->n_magic == SMB_NODE_MAGIC);
+ *odp = NULL;
if (SMB_TREE_CONTAINS_NODE(sr, unode) == 0 ||
- SMB_TREE_HAS_ACCESS(sr, ACE_LIST_DIRECTORY) == 0) {
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
- ERRDOS, ERROR_ACCESS_DENIED);
- return (0);
- }
+ SMB_TREE_HAS_ACCESS(sr, ACE_LIST_DIRECTORY) == 0)
+ return (NT_STATUS_ACCESS_DENIED);
+
cr = zone_kcred();
/* find the xattrdir vnode */
rc = smb_vop_lookup_xattrdir(unode->vp, &xattr_dvp, LOOKUP_XATTR, cr);
- if (rc != 0) {
- smbsr_errno(sr, rc);
- return (0);
- }
+ if (rc != 0)
+ return (smb_errno2status(rc));
/* lookup the xattrdir's smb_node */
xattr_dnode = smb_node_lookup(sr, NULL, cr, xattr_dvp, XATTR_DIR,
unode, NULL);
VN_RELE(xattr_dvp);
- if (xattr_dnode == NULL) {
- smbsr_error(sr, NT_STATUS_NO_MEMORY,
- ERRDOS, ERROR_NOT_ENOUGH_MEMORY);
- return (0);
- }
+ if (xattr_dnode == NULL)
+ return (NT_STATUS_NO_MEMORY);
(void) snprintf(pattern, sizeof (pattern), "%s*", SMB_STREAM_PREFIX);
- odid = smb_odir_create(sr, xattr_dnode, pattern, SMB_SEARCH_ATTRIBUTES,
- cr);
+ *odp = smb_odir_create(sr, xattr_dnode, pattern,
+ SMB_SEARCH_ATTRIBUTES, 0, cr);
+
smb_node_release(xattr_dnode);
- return (odid);
+ return (0);
}
/*
@@ -483,8 +508,9 @@ smb_odir_close(smb_odir_t *od)
* 0 - success.
* - If a matching entry was found eof will be B_FALSE and
* odirent will be populated.
- * - If there are no matching entries eof will be B_TRUE.
- * -1 - error, error details set in sr.
+ * ENOENT
+ * - If we've scanned to the end, eof will be B_TRUE.
+ * errno - other errors
*/
int
smb_odir_read(smb_request_t *sr, smb_odir_t *od,
@@ -509,7 +535,7 @@ smb_odir_read(smb_request_t *sr, smb_odir_t *od,
case SMB_ODIR_STATE_CLOSED:
default:
mutex_exit(&od->d_mutex);
- return (-1);
+ return (EBADF);
}
for (;;) {
@@ -527,10 +553,9 @@ smb_odir_read(smb_request_t *sr, smb_odir_t *od,
return (0);
case ENOENT:
*eof = B_TRUE;
- return (0);
+ /* FALLTHROUGH */
default:
- smbsr_errno(sr, rc);
- return (-1);
+ return (rc);
}
}
@@ -554,8 +579,9 @@ smb_odir_read(smb_request_t *sr, smb_odir_t *od,
* 0 - success.
* - If a matching entry was found eof will be B_FALSE and
* fileinfo will be populated.
- * - If there are no matching entries eof will be B_TRUE.
- * -1 - error, error details set in sr.
+ * ENOENT
+ * - If at end of dir, eof will be B_TRUE.
+ * errno - other error
*/
int
smb_odir_read_fileinfo(smb_request_t *sr, smb_odir_t *od,
@@ -581,7 +607,7 @@ smb_odir_read_fileinfo(smb_request_t *sr, smb_odir_t *od,
case SMB_ODIR_STATE_CLOSED:
default:
mutex_exit(&od->d_mutex);
- return (-1);
+ return (EBADF);
}
if ((od->d_flags & SMB_ODIR_FLAG_WILDCARDS) == 0) {
@@ -621,10 +647,9 @@ smb_odir_read_fileinfo(smb_request_t *sr, smb_odir_t *od,
return (0);
case ENOENT:
*eof = 1; /* per. FindFirst, FindNext spec. */
- return (0);
+ /* FALLTHROUGH */
default:
- smbsr_errno(sr, rc);
- return (-1);
+ return (rc);
}
}
@@ -641,13 +666,14 @@ smb_odir_read_fileinfo(smb_request_t *sr, smb_odir_t *od,
* - If a matching entry was found eof will be B_FALSE and
* sinfo will be populated.
* - If there are no matching entries eof will be B_TRUE.
- * -1 - error, error details set in sr.
+ * errno - error
*/
int
smb_odir_read_streaminfo(smb_request_t *sr, smb_odir_t *od,
smb_streaminfo_t *sinfo, boolean_t *eof)
{
int rc;
+ cred_t *kcr;
smb_odirent_t *odirent;
smb_node_t *fnode;
smb_attr_t attr;
@@ -658,6 +684,8 @@ smb_odir_read_streaminfo(smb_request_t *sr, smb_odir_t *od,
ASSERT(od->d_magic == SMB_ODIR_MAGIC);
ASSERT(sinfo);
+ kcr = zone_kcred();
+
mutex_enter(&od->d_mutex);
ASSERT(od->d_refcnt > 0);
@@ -669,7 +697,7 @@ smb_odir_read_streaminfo(smb_request_t *sr, smb_odir_t *od,
case SMB_ODIR_STATE_CLOSED:
default:
mutex_exit(&od->d_mutex);
- return (-1);
+ return (EBADF);
}
/* Check that odir represents an xattr directory */
@@ -695,9 +723,13 @@ smb_odir_read_streaminfo(smb_request_t *sr, smb_odir_t *od,
rc = smb_fsop_lookup(sr, od->d_cred, 0, od->d_tree->t_snode,
od->d_dnode, odirent->od_name, &fnode);
if (rc == 0) {
+ /*
+ * We just need the file sizes, and don't want
+ * EACCES failures here, so use kcred and pass
+ * NULL as the sr to skip sr->fid-ofile checks.
+ */
attr.sa_mask = SMB_AT_SIZE | SMB_AT_ALLOCSZ;
- rc = smb_node_getattr(sr, fnode, od->d_cred,
- NULL, &attr);
+ rc = smb_node_getattr(NULL, fnode, kcr, NULL, &attr);
smb_node_release(fnode);
}
@@ -722,8 +754,7 @@ smb_odir_read_streaminfo(smb_request_t *sr, smb_odir_t *od,
*eof = B_TRUE;
return (0);
default:
- smbsr_errno(sr, rc);
- return (-1);
+ return (rc);
}
}
@@ -770,8 +801,8 @@ smb_odir_save_fname(smb_odir_t *od, uint32_t cookie, const char *fname)
/*
* smb_odir_resume_at
*
- * If SMB_ODIR_FLAG_WILDCARDS is not set the search is for a single
- * file and should not be resumed.
+ * If SMB_ODIR_FLAG_WILDCARDS is not set, and we're rewinding,
+ * assume we're no longer at EOF.
*
* Wildcard searching can be resumed from:
* - the cookie saved at a specified index (SMBsearch, SMBfind).
@@ -787,18 +818,20 @@ smb_odir_save_fname(smb_odir_t *od, uint32_t cookie, const char *fname)
void
smb_odir_resume_at(smb_odir_t *od, smb_odir_resume_t *resume)
{
+ uint64_t save_offset;
+
ASSERT(od);
ASSERT(od->d_magic == SMB_ODIR_MAGIC);
ASSERT(resume);
- mutex_enter(&od->d_mutex);
-
if ((od->d_flags & SMB_ODIR_FLAG_WILDCARDS) == 0) {
- od->d_eof = B_TRUE;
- mutex_exit(&od->d_mutex);
+ if (resume->or_type == SMB_ODIR_RESUME_COOKIE)
+ od->d_eof = B_FALSE;
return;
}
+ mutex_enter(&od->d_mutex);
+ save_offset = od->d_offset;
switch (resume->or_type) {
default:
@@ -841,9 +874,11 @@ smb_odir_resume_at(smb_odir_t *od, smb_odir_resume_t *resume)
break;
}
- /* Force a vop_readdir to refresh d_buf */
- od->d_bufptr = NULL;
- od->d_eof = B_FALSE;
+ if (od->d_offset != save_offset) {
+ /* Force a vop_readdir to refresh d_buf */
+ od->d_bufptr = NULL;
+ od->d_eof = B_FALSE;
+ }
mutex_exit(&od->d_mutex);
}
@@ -855,13 +890,12 @@ smb_odir_resume_at(smb_odir_t *od, smb_odir_resume_t *resume)
* smb_odir_create
* Allocate and populate an odir obect and add it to the tree's list.
*/
-static uint16_t
+static smb_odir_t *
smb_odir_create(smb_request_t *sr, smb_node_t *dnode,
- char *pattern, uint16_t sattr, cred_t *cr)
+ const char *pattern, uint16_t sattr, uint16_t odid, cred_t *cr)
{
smb_odir_t *od;
smb_tree_t *tree;
- uint16_t odid;
ASSERT(sr);
ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
@@ -872,18 +906,17 @@ smb_odir_create(smb_request_t *sr, smb_node_t *dnode,
tree = sr->tid_tree;
- if (smb_idpool_alloc(&tree->t_odid_pool, &odid)) {
- smbsr_error(sr, NT_STATUS_TOO_MANY_OPENED_FILES,
- ERRDOS, ERROR_TOO_MANY_OPEN_FILES);
- return (0);
- }
-
od = kmem_cache_alloc(smb_cache_odir, KM_SLEEP);
bzero(od, sizeof (smb_odir_t));
mutex_init(&od->d_mutex, NULL, MUTEX_DEFAULT, NULL);
- od->d_refcnt = 0;
- od->d_state = SMB_ODIR_STATE_OPEN;
+
+ /*
+ * Return this to the caller as if they had done
+ * smb_tree_lookup_odir() to obtain the odir.
+ */
+ od->d_refcnt = 1;
+ od->d_state = SMB_ODIR_STATE_IN_USE;
od->d_magic = SMB_ODIR_MAGIC;
od->d_opened_by_pid = sr->smb_pid;
od->d_session = tree->t_session;
@@ -922,7 +955,32 @@ smb_odir_create(smb_request_t *sr, smb_node_t *dnode,
smb_llist_exit(&tree->t_odir_list);
atomic_inc_32(&tree->t_session->s_dir_cnt);
- return (odid);
+ return (od);
+}
+
+/*
+ * Set a new pattern, attributes, and rewind.
+ */
+void
+smb_odir_reopen(smb_odir_t *od, const char *pattern, uint16_t sattr)
+{
+
+ SMB_ODIR_VALID(od);
+
+ mutex_enter(&od->d_mutex);
+ od->d_sattr = sattr;
+ (void) strlcpy(od->d_pattern, pattern, sizeof (od->d_pattern));
+ if (smb_contains_wildcards(od->d_pattern))
+ od->d_flags |= SMB_ODIR_FLAG_WILDCARDS;
+ else
+ od->d_flags &= ~SMB_ODIR_FLAG_WILDCARDS;
+
+ /* Internal smb_odir_resume_at */
+ od->d_offset = 0;
+ od->d_bufptr = NULL;
+ od->d_eof = B_FALSE;
+
+ mutex_exit(&od->d_mutex);
}
/*
@@ -944,7 +1002,8 @@ smb_odir_delete(void *arg)
tree = od->d_tree;
smb_llist_enter(&tree->t_odir_list, RW_WRITER);
smb_llist_remove(&tree->t_odir_list, od);
- smb_idpool_free(&tree->t_odid_pool, od->d_odid);
+ if (od->d_odid != 0)
+ smb_idpool_free(&tree->t_odid_pool, od->d_odid);
atomic_dec_32(&tree->t_session->s_dir_cnt);
smb_llist_exit(&tree->t_odir_list);
@@ -1134,7 +1193,7 @@ smb_odir_single_fileinfo(smb_request_t *sr, smb_odir_t *od,
bzero(&attr, sizeof (attr));
attr.sa_mask = SMB_AT_ALL;
- rc = smb_node_getattr(sr, fnode, zone_kcred(), NULL, &attr);
+ rc = smb_node_getattr(NULL, fnode, zone_kcred(), NULL, &attr);
if (rc != 0) {
smb_node_release(fnode);
return (rc);
@@ -1147,7 +1206,7 @@ smb_odir_single_fileinfo(smb_request_t *sr, smb_odir_t *od,
smb_node_release(fnode);
fnode = tgt_node;
attr.sa_mask = SMB_AT_ALL;
- rc = smb_node_getattr(sr, fnode, zone_kcred(), NULL, &attr);
+ rc = smb_node_getattr(NULL, fnode, zone_kcred(), NULL, &attr);
if (rc != 0) {
smb_node_release(fnode);
return (rc);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_ofile.c b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
index a21461efe3..34ef5dc526 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_ofile.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_ofile.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 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -236,7 +236,12 @@ smb_ofile_open(
ASSERT(ftype == SMB_FTYPE_DISK); /* Regular file, not a pipe */
ASSERT(node);
- if (of->f_granted_access == FILE_EXECUTE)
+ /*
+ * Note that the common open path often adds bits like
+ * READ_CONTROL, so the logic "is this open exec-only"
+ * needs to look at only the FILE_DATA_ALL bits.
+ */
+ if ((of->f_granted_access & FILE_DATA_ALL) == FILE_EXECUTE)
of->f_flags |= SMB_OFLAGS_EXECONLY;
bzero(&attr, sizeof (smb_attr_t));
@@ -317,6 +322,7 @@ errout:
void
smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
{
+ smb_attr_t *pa;
timestruc_t now;
uint32_t flags = 0;
@@ -331,18 +337,22 @@ smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
of->f_state = SMB_OFILE_STATE_CLOSING;
mutex_exit(&of->f_mutex);
- if (of->f_ftype == SMB_FTYPE_MESG_PIPE) {
+ switch (of->f_ftype) {
+ case SMB_FTYPE_BYTE_PIPE:
+ case SMB_FTYPE_MESG_PIPE:
smb_opipe_close(of);
smb_server_dec_pipes(of->f_server);
- } else {
- smb_attr_t *pa = &of->f_pending_attr;
+ break;
+ case SMB_FTYPE_DISK:
+ case SMB_FTYPE_PRINTER:
/*
* In here we make changes to of->f_pending_attr
* while not holding of->f_mutex. This is OK
* because we've changed f_state to CLOSING,
* so no more threads will take this path.
*/
+ pa = &of->f_pending_attr;
if (mtime_sec != 0) {
pa->sa_vattr.va_mtime.tv_sec = mtime_sec;
pa->sa_mask |= SMB_AT_MTIME;
@@ -377,6 +387,12 @@ smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
(void) smb_fsop_close(of->f_node, of->f_mode,
of->f_cr);
smb_oplock_release(of->f_node, of);
+ } else {
+ /*
+ * If there was an odir, close it.
+ */
+ if (of->f_odir != NULL)
+ smb_odir_close(of->f_odir);
}
if (smb_node_dec_open_ofiles(of->f_node) == 0) {
/*
@@ -415,6 +431,7 @@ smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
smb_notify_file_closed(of);
smb_server_dec_files(of->f_server);
+ break;
}
atomic_dec_32(&of->f_tree->t_open_files);
@@ -932,14 +949,21 @@ smb_ofile_delete(void *arg)
mutex_enter(&of->f_mutex);
mutex_exit(&of->f_mutex);
- if (of->f_ftype == SMB_FTYPE_MESG_PIPE) {
+ switch (of->f_ftype) {
+ case SMB_FTYPE_BYTE_PIPE:
+ case SMB_FTYPE_MESG_PIPE:
smb_opipe_dealloc(of->f_pipe);
of->f_pipe = NULL;
- } else {
- ASSERT(of->f_ftype == SMB_FTYPE_DISK);
- ASSERT(of->f_node != NULL);
+ break;
+ case SMB_FTYPE_DISK:
+ if (of->f_odir != NULL)
+ smb_odir_release(of->f_odir);
smb_node_rem_ofile(of->f_node, of);
smb_node_release(of->f_node);
+ break;
+ default:
+ ASSERT(!"f_ftype");
+ break;
}
of->f_magic = (uint32_t)~SMB_OFILE_MAGIC;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_open_andx.c b/usr/src/uts/common/fs/smbsrv/smb_open_andx.c
index 977fc0bbbe..e370f74747 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_open_andx.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_open_andx.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 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
@@ -244,6 +244,7 @@ smb_com_open(smb_request_t *sr)
struct open_param *op = &sr->arg.open;
smb_ofile_t *of;
smb_attr_t attr;
+ uint32_t status;
uint16_t file_attr;
int rc;
@@ -271,8 +272,11 @@ smb_com_open(smb_request_t *sr)
return (SDRC_ERROR);
}
- if (smb_common_open(sr) != NT_STATUS_SUCCESS)
+ status = smb_common_open(sr);
+ if (status != NT_STATUS_SUCCESS) {
+ smbsr_status(sr, status, 0, 0);
return (SDRC_ERROR);
+ }
/*
* NB: after the above smb_common_open() success,
@@ -322,6 +326,7 @@ smb_pre_open_andx(smb_request_t *sr)
{
struct open_param *op = &sr->arg.open;
uint16_t flags;
+ uint32_t alloc_size;
uint32_t creation_time;
uint16_t file_attr, sattr;
int rc;
@@ -330,12 +335,13 @@ smb_pre_open_andx(smb_request_t *sr)
rc = smbsr_decode_vwv(sr, "b.wwwwwlwll4.", &sr->andx_com,
&sr->andx_off, &flags, &op->omode, &sattr,
- &file_attr, &creation_time, &op->ofun, &op->dsize, &op->timeo);
+ &file_attr, &creation_time, &op->ofun, &alloc_size, &op->timeo);
if (rc == 0) {
rc = smbsr_decode_data(sr, "%u", sr, &op->fqi.fq_path.pn_path);
op->dattr = file_attr;
+ op->dsize = alloc_size;
if (flags & 2)
op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
@@ -369,6 +375,7 @@ smb_com_open_andx(smb_request_t *sr)
{
struct open_param *op = &sr->arg.open;
smb_ofile_t *of;
+ uint32_t status;
uint16_t file_attr;
smb_attr_t attr;
int rc;
@@ -393,8 +400,11 @@ smb_com_open_andx(smb_request_t *sr)
return (SDRC_ERROR);
}
- if (smb_common_open(sr) != NT_STATUS_SUCCESS)
+ status = smb_common_open(sr);
+ if (status != NT_STATUS_SUCCESS) {
+ smbsr_status(sr, status, 0, 0);
return (SDRC_ERROR);
+ }
/*
* NB: after the above smb_common_open() success,
@@ -472,6 +482,7 @@ smb_com_trans2_open2(smb_request_t *sr, smb_xa_t *xa)
uint32_t alloc_size;
uint16_t flags;
uint16_t file_attr;
+ uint32_t status;
int rc;
bzero(op, sizeof (sr->arg.open));
@@ -511,8 +522,11 @@ smb_com_trans2_open2(smb_request_t *sr, smb_xa_t *xa)
}
op->op_oplock_levelII = B_FALSE;
- if (smb_common_open(sr) != NT_STATUS_SUCCESS)
+ status = smb_common_open(sr);
+ if (status != NT_STATUS_SUCCESS) {
+ smbsr_status(sr, status, 0, 0);
return (SDRC_ERROR);
+ }
if (op->op_oplock_level != SMB_OPLOCK_NONE)
op->action_taken |= SMB_OACT_LOCK;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_opipe.c b/usr/src/uts/common/fs/smbsrv/smb_opipe.c
index a77535e2f7..273a2f7297 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_opipe.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_opipe.c
@@ -35,6 +35,9 @@
#include <sys/filio.h>
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_xdr.h>
+#include <smbsrv/winioctl.h>
+
+static uint32_t smb_opipe_transceive(smb_request_t *, smb_fsctl_t *);
/*
* Allocate a new opipe and return it, or NULL, in which case
@@ -397,12 +400,12 @@ out:
}
int
-smb_opipe_get_nread(smb_request_t *sr, int *nread)
+smb_opipe_ioctl(smb_request_t *sr, int cmd, void *arg, int *rvalp)
{
smb_ofile_t *ofile;
smb_opipe_t *opipe;
ksocket_t sock;
- int rc, trval;
+ int rc;
ofile = sr->fid_ofile;
ASSERT(ofile->f_ftype == SMB_FTYPE_MESG_PIPE);
@@ -417,10 +420,138 @@ smb_opipe_get_nread(smb_request_t *sr, int *nread)
if (sock == NULL)
return (EBADF);
- rc = ksocket_ioctl(sock, FIONREAD, (intptr_t)nread, &trval,
- ofile->f_cr);
+ rc = ksocket_ioctl(sock, cmd, (intptr_t)arg, rvalp, ofile->f_cr);
ksocket_rele(sock);
return (rc);
}
+
+/*
+ * Get the smb_attr_t for a named pipe.
+ * Caller has already cleared to zero.
+ */
+int
+smb_opipe_getattr(smb_ofile_t *of, smb_attr_t *ap)
+{
+
+ if (of->f_pipe == NULL)
+ return (EINVAL);
+
+ ap->sa_vattr.va_type = VFIFO;
+ ap->sa_vattr.va_nlink = 1;
+ ap->sa_dosattr = FILE_ATTRIBUTE_NORMAL;
+ ap->sa_allocsz = 0x1000LL;
+
+ return (0);
+}
+
+int
+smb_opipe_getname(smb_ofile_t *of, char *buf, size_t buflen)
+{
+ smb_opipe_t *opipe;
+
+ if ((opipe = of->f_pipe) == NULL)
+ return (EINVAL);
+
+ (void) snprintf(buf, buflen, "\\%s", opipe->p_name);
+ return (0);
+}
+
+/*
+ * Handler for smb2_ioctl
+ */
+/* ARGSUSED */
+uint32_t
+smb_opipe_fsctl(smb_request_t *sr, smb_fsctl_t *fsctl)
+{
+ uint32_t status;
+
+ switch (fsctl->CtlCode) {
+ case FSCTL_PIPE_TRANSCEIVE:
+ status = smb_opipe_transceive(sr, fsctl);
+ break;
+
+ case FSCTL_PIPE_PEEK:
+ case FSCTL_PIPE_WAIT:
+ /* XXX todo */
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+
+ default:
+ ASSERT(!"CtlCode");
+ status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+ return (status);
+}
+
+static uint32_t
+smb_opipe_transceive(smb_request_t *sr, smb_fsctl_t *fsctl)
+{
+ smb_vdb_t vdb;
+ smb_ofile_t *ofile;
+ struct mbuf *mb;
+ uint32_t status;
+ int len, rc;
+
+ /*
+ * Caller checked that this is the IPC$ share,
+ * and that this call has a valid open handle.
+ * Just check the type.
+ */
+ ofile = sr->fid_ofile;
+ if (ofile->f_ftype != SMB_FTYPE_MESG_PIPE)
+ return (NT_STATUS_INVALID_HANDLE);
+
+ rc = smb_mbc_decodef(fsctl->in_mbc, "#B",
+ fsctl->InputCount, &vdb);
+ if (rc != 0) {
+ /* Not enough data sent. */
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ rc = smb_opipe_write(sr, &vdb.vdb_uio);
+ if (rc != 0)
+ return (smb_errno2status(rc));
+
+ vdb.vdb_tag = 0;
+ vdb.vdb_uio.uio_iov = &vdb.vdb_iovec[0];
+ vdb.vdb_uio.uio_iovcnt = MAX_IOVEC;
+ vdb.vdb_uio.uio_segflg = UIO_SYSSPACE;
+ vdb.vdb_uio.uio_extflg = UIO_COPY_DEFAULT;
+ vdb.vdb_uio.uio_loffset = (offset_t)0;
+ vdb.vdb_uio.uio_resid = fsctl->MaxOutputResp;
+ mb = smb_mbuf_allocate(&vdb.vdb_uio);
+
+ rc = smb_opipe_read(sr, &vdb.vdb_uio);
+ if (rc != 0) {
+ m_freem(mb);
+ return (smb_errno2status(rc));
+ }
+
+ len = fsctl->MaxOutputResp - vdb.vdb_uio.uio_resid;
+ smb_mbuf_trim(mb, len);
+ MBC_ATTACH_MBUF(fsctl->out_mbc, mb);
+
+ /*
+ * If the output buffer holds a partial pipe message,
+ * we're supposed to return NT_STATUS_BUFFER_OVERFLOW.
+ * As we don't have message boundary markers, the best
+ * we can do is return that status when we have ALL of:
+ * Output buffer was < SMB_PIPE_MAX_MSGSIZE
+ * We filled the output buffer (resid==0)
+ * There's more data (ioctl FIONREAD)
+ */
+ status = NT_STATUS_SUCCESS;
+ if (fsctl->MaxOutputResp < SMB_PIPE_MAX_MSGSIZE &&
+ vdb.vdb_uio.uio_resid == 0) {
+ int nread = 0, trval;
+ rc = smb_opipe_ioctl(sr, FIONREAD, &nread, &trval);
+ if (rc == 0 && nread != 0)
+ status = NT_STATUS_BUFFER_OVERFLOW;
+ }
+
+ return (status);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_oplock.c b/usr/src/uts/common/fs/smbsrv/smb_oplock.c
index b2d77a0be4..39677ffdd5 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_oplock.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_oplock.c
@@ -19,8 +19,8 @@
* CDDL HEADER END
*/
/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -60,80 +60,9 @@ static void smb_oplock_remove_grant(smb_node_t *, smb_oplock_grant_t *);
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 smb_oplock_break_t *smb_oplock_create_break(smb_node_t *);
-static smb_oplock_break_t *smb_oplock_get_break(void);
-static void smb_oplock_delete_break(smb_oplock_break_t *);
-static void smb_oplock_process_levelII_break(smb_node_t *);
-
-static void smb_oplock_break_thread();
-
-/* levelII oplock break requests (smb_oplock_break_t) */
-static boolean_t smb_oplock_initialized = B_FALSE;
-static kmem_cache_t *smb_oplock_break_cache = NULL;
-static smb_llist_t smb_oplock_breaks;
-static smb_thread_t smb_oplock_thread;
-/* shared by all zones */
-
-/*
- * smb_oplock_init
- *
- * This function is not multi-thread safe. The caller must make sure only one
- * thread makes the call.
- */
-int
-smb_oplock_init(void)
-{
- int rc;
-
- if (smb_oplock_initialized)
- return (0);
-
- smb_oplock_break_cache = kmem_cache_create("smb_oplock_break_cache",
- sizeof (smb_oplock_break_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
-
- smb_llist_constructor(&smb_oplock_breaks, sizeof (smb_oplock_break_t),
- offsetof(smb_oplock_break_t, ob_lnd));
-
- smb_thread_init(&smb_oplock_thread, "smb_thread_oplock_break",
- smb_oplock_break_thread, NULL, smbsrv_notify_pri);
-
- rc = smb_thread_start(&smb_oplock_thread);
- if (rc != 0) {
- smb_thread_destroy(&smb_oplock_thread);
- smb_llist_destructor(&smb_oplock_breaks);
- kmem_cache_destroy(smb_oplock_break_cache);
- return (rc);
- }
-
- smb_oplock_initialized = B_TRUE;
- return (0);
-}
-
-/*
- * smb_oplock_fini
- * This function is not multi-thread safe. The caller must make sure only one
- * thread makes the call.
- */
-void
-smb_oplock_fini(void)
-{
- smb_oplock_break_t *ob;
-
- if (!smb_oplock_initialized)
- return;
-
- smb_thread_stop(&smb_oplock_thread);
- smb_thread_destroy(&smb_oplock_thread);
-
- while ((ob = smb_llist_head(&smb_oplock_breaks)) != NULL) {
- SMB_OPLOCK_BREAK_VALID(ob);
- smb_llist_remove(&smb_oplock_breaks, ob);
- smb_oplock_delete_break(ob);
- }
- smb_llist_destructor(&smb_oplock_breaks);
-
- kmem_cache_destroy(smb_oplock_break_cache);
-}
+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_break_levelII_locked(smb_node_t *);
/*
* smb_oplock_install_fem
@@ -172,6 +101,16 @@ smb_oplock_uninstall_fem(smb_node_t *node)
}
/*
+ * This provides a way to fully disable oplocks, i.e. for testing.
+ * You _really_ do _not_ want to turn this off, because if you do,
+ * the clients send you very small read requests, and a _lot_ more
+ * of them. The skc_oplock_enable parameter can be used to enable
+ * or disable exclusive oplocks. Disabling that can be helpful
+ * when there are clients not responding to oplock breaks.
+ */
+int smb_oplocks_enabled = 1;
+
+/*
* smb_oplock_acquire
*
* Attempt to acquire an oplock. Clients will request EXCLUSIVE or BATCH,
@@ -212,7 +151,7 @@ smb_oplock_acquire(smb_request_t *sr, smb_node_t *node, smb_ofile_t *ofile)
tree = SMB_OFILE_GET_TREE(ofile);
session = SMB_OFILE_GET_SESSION(ofile);
- if (!smb_tree_has_feature(tree, SMB_TREE_OPLOCKS) ||
+ if (smb_oplocks_enabled == 0 ||
(op->op_oplock_level == SMB_OPLOCK_NONE) ||
((op->op_oplock_level == SMB_OPLOCK_BATCH) &&
SMB_IS_STREAM(node))) {
@@ -226,8 +165,20 @@ smb_oplock_acquire(smb_request_t *sr, smb_node_t *node, smb_ofile_t *ofile)
mutex_enter(&ol->ol_mutex);
smb_oplock_wait(node);
+ /*
+ * Even if there are no other opens, we might want to
+ * grant only a Level II (shared) oplock so we avoid
+ * ever granting exclusive oplocks.
+ *
+ * Borrowing the SMB_TREE_OPLOCKS flag to enable/disable
+ * exclusive oplocks (for now). See skc_oplock_enable,
+ * which can now be taken as "exclusive oplock enable".
+ * Should rename this parameter, and/or implement a new
+ * multi-valued parameter for oplock enables.
+ */
if ((node->n_open_count > 1) ||
(node->n_opening_count > 1) ||
+ !smb_tree_has_feature(tree, SMB_TREE_OPLOCKS) ||
smb_vop_other_opens(node->vp, ofile->f_mode)) {
/*
* There are other opens.
@@ -341,8 +292,7 @@ smb_oplock_break(smb_request_t *sr, smb_node_t *node, uint32_t flags)
switch (ol->ol_break) {
case SMB_OPLOCK_NO_BREAK:
ol->ol_break = brk;
- smb_session_oplock_break(og->og_session,
- og->og_tid, og->og_fid, brk);
+ smb_oplock_sched_async_break(og, brk);
break;
case SMB_OPLOCK_BREAK_TO_LEVEL_II:
if (brk == SMB_OPLOCK_BREAK_TO_NONE)
@@ -373,99 +323,156 @@ smb_oplock_break(smb_request_t *sr, smb_node_t *node, uint32_t flags)
/*
* smb_oplock_break_levelII
*
+ * This is called after a file is modified in some way. If there are
+ * LevelII (shared) oplocks, break those to none. If there is an
+ * exclusive oplock, there can be no LevelII oplocks, so do nothing.
+ *
* LevelII (shared) oplock breaks are processed asynchronously.
* Unlike exclusive oplock breaks, the thread initiating the break
* is NOT blocked while the request is processed.
*
- * Create an oplock_break_request and add it to the list for async
- * processing.
+ * There may be a thread with exclusive rights to oplock state for
+ * this node (via ol_xthread in smb_oplock_wait) and if so, we must
+ * avoid breaking oplocks until that's out of the way. However, we
+ * really don't want to block here, so when ol_xthread is set, we'll
+ * just mark that a "break level II to none" is pending, and let the
+ * exclusive thread do this work when it's done being exclusive.
*/
void
smb_oplock_break_levelII(smb_node_t *node)
{
- smb_oplock_break_t *ob;
+ smb_oplock_t *ol;
- ob = smb_oplock_create_break(node);
+ ol = &node->n_oplock;
+ mutex_enter(&ol->ol_mutex);
- smb_llist_enter(&smb_oplock_breaks, RW_WRITER);
- smb_llist_insert_tail(&smb_oplock_breaks, ob);
- smb_llist_exit(&smb_oplock_breaks);
+ /* Instead of: smb_oplock_wait() ... */
+ if (ol->ol_xthread != NULL) {
+ /* Defer the call to smb_oplock_broadcast(). */
+ ol->ol_brk_pending = SMB_OPLOCK_BREAK_TO_NONE;
+ } else {
+ /* Equivalent of smb_oplock_wait() done. */
+ smb_oplock_break_levelII_locked(node);
+ }
- smb_thread_signal(&smb_oplock_thread);
+ mutex_exit(&ol->ol_mutex);
}
/*
- * smb_oplock_break_thread
+ * smb_oplock_break_levelII_locked
+ * Internal helper for smb_oplock_break_levelII()
*
- * The smb_oplock_thread is woken when an oplock break request is
- * added to the list of pending levelII oplock break requests.
- * Gets the oplock break request from the list, processes it and
- * deletes it.
+ * Called with the oplock mutex already held, and _after_
+ * (the equivalent of) an smb_oplock_wait().
*/
-/*ARGSUSED*/
static void
-smb_oplock_break_thread(smb_thread_t *thread, void *arg)
+smb_oplock_break_levelII_locked(smb_node_t *node)
{
- smb_oplock_break_t *ob;
+ smb_oplock_t *ol;
+ smb_oplock_grant_t *og;
+ list_t *grants;
- while (smb_thread_continue(thread)) {
- while ((ob = smb_oplock_get_break()) != NULL) {
- smb_oplock_process_levelII_break(ob->ob_node);
- smb_oplock_delete_break(ob);
- }
+ ol = &node->n_oplock;
+ grants = &ol->ol_grants;
+
+ ASSERT(MUTEX_HELD(&ol->ol_mutex));
+ ASSERT(ol->ol_xthread == NULL);
+
+ while ((og = list_head(grants)) != NULL) {
+ SMB_OPLOCK_GRANT_VALID(og);
+
+ /*
+ * If there's an exclusive oplock, there are
+ * no LevelII oplocks, so do nothing.
+ */
+ if (SMB_OPLOCK_IS_EXCLUSIVE(og->og_level))
+ break;
+
+ smb_oplock_sched_async_break(og, SMB_OPLOCK_BREAK_TO_NONE);
+ smb_oplock_remove_grant(node, og);
+ smb_oplock_clear_grant(og);
}
}
/*
- * smb_oplock_get_break
- *
- * Remove and return the next oplock break request from the list
+ * Schedule a call to smb_session_oplock_break
+ * using an smb_request on the owning session.
*/
-static smb_oplock_break_t *
-smb_oplock_get_break(void)
+static void
+smb_oplock_sched_async_break(smb_oplock_grant_t *og, uint8_t brk)
{
- smb_oplock_break_t *ob;
-
- smb_llist_enter(&smb_oplock_breaks, RW_WRITER);
- if ((ob = smb_llist_head(&smb_oplock_breaks)) != NULL) {
- SMB_OPLOCK_BREAK_VALID(ob);
- smb_llist_remove(&smb_oplock_breaks, ob);
- }
- smb_llist_exit(&smb_oplock_breaks);
- return (ob);
+ smb_request_t *sr;
+ smb_ofile_t *ofile;
+
+ /*
+ * 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. (Also hold the tree and user.)
+ * 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;
+ if (!smb_ofile_hold(ofile))
+ return;
+ smb_tree_hold_internal(ofile->f_tree);
+ smb_user_hold_internal(ofile->f_user);
+
+ sr = smb_request_alloc(og->og_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;
+
+ 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);
}
/*
- * smb_oplock_process_levelII_break
+ * smb_oplock_exec_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.
*/
-void
-smb_oplock_process_levelII_break(smb_node_t *node)
+static void
+smb_oplock_exec_async_break(void *arg)
{
- smb_oplock_t *ol;
- smb_oplock_grant_t *og;
- list_t *grants;
+ smb_request_t *sr = arg;
+ smb_oplock_grant_t *og = &sr->arg.olbrk;
- if (!smb_oplock_levelII)
- return;
+ SMB_REQ_VALID(sr);
+ SMB_OPLOCK_GRANT_VALID(og);
- ol = &node->n_oplock;
- mutex_enter(&ol->ol_mutex);
- smb_oplock_wait(node);
- grants = &node->n_oplock.ol_grants;
+ mutex_enter(&sr->sr_mutex);
+ sr->sr_worker = curthread;
+ sr->sr_time_active = gethrtime();
- while ((og = list_head(grants)) != NULL) {
- SMB_OPLOCK_GRANT_VALID(og);
+ switch (sr->sr_state) {
+ case SMB_REQ_STATE_SUBMITTED:
+ sr->sr_state = SMB_REQ_STATE_ACTIVE;
+ mutex_exit(&sr->sr_mutex);
- if (SMB_OPLOCK_IS_EXCLUSIVE(og->og_level))
- break;
+ /*
+ * This is where we actually do the deferred work
+ * requested by smb_oplock_sched_async_break().
+ */
+ smb_session_oplock_break(sr, og->og_breaking);
- smb_session_oplock_break(og->og_session,
- og->og_tid, og->og_fid, SMB_OPLOCK_BREAK_TO_NONE);
- smb_oplock_remove_grant(node, og);
- smb_oplock_clear_grant(og);
+ mutex_enter(&sr->sr_mutex);
+ /* FALLTHROUGH */
+
+ default: /* typically cancelled */
+ sr->sr_state = SMB_REQ_STATE_COMPLETED;
+ mutex_exit(&sr->sr_mutex);
}
- mutex_exit(&ol->ol_mutex);
+ smb_request_free(sr);
}
/*
@@ -582,7 +589,6 @@ smb_oplock_ack(smb_node_t *node, smb_ofile_t *of, uint8_t brk)
{
smb_oplock_t *ol;
smb_oplock_grant_t *og;
- boolean_t brk_to_none = B_FALSE;
ol = &node->n_oplock;
mutex_enter(&ol->ol_mutex);
@@ -604,7 +610,8 @@ smb_oplock_ack(smb_node_t *node, smb_ofile_t *of, uint8_t brk)
} else {
/* SMB_OPLOCK_BREAK_TO_NONE */
og->og_level = SMB_OPLOCK_NONE;
- brk_to_none = B_TRUE;
+ smb_oplock_sched_async_break(og,
+ SMB_OPLOCK_BREAK_TO_NONE);
}
break;
default:
@@ -619,22 +626,18 @@ smb_oplock_ack(smb_node_t *node, smb_ofile_t *of, uint8_t brk)
ol->ol_break = SMB_OPLOCK_NO_BREAK;
cv_broadcast(&ol->ol_cv);
- if (brk_to_none) {
- smb_session_oplock_break(of->f_session,
- of->f_tree->t_tid, of->f_fid,
- SMB_OPLOCK_BREAK_TO_NONE);
- }
-
mutex_exit(&ol->ol_mutex);
}
/*
* smb_oplock_broadcast
*
+ * Called when an open with oplock request completes.
+ *
* ol->ol_xthread identifies the thread that was performing an oplock
* acquire. Other threads may be blocked awaiting completion of the
* acquire.
- * If the calling thread is ol_ol_xthread, wake any waiting threads.
+ * If the calling thread is ol_xthread, wake any waiting threads.
*/
void
smb_oplock_broadcast(smb_node_t *node)
@@ -647,6 +650,10 @@ smb_oplock_broadcast(smb_node_t *node)
mutex_enter(&ol->ol_mutex);
if ((ol->ol_xthread != NULL) && (ol->ol_xthread == curthread)) {
ol->ol_xthread = NULL;
+ if (ol->ol_brk_pending) {
+ ol->ol_brk_pending = 0;
+ smb_oplock_break_levelII_locked(node);
+ }
cv_broadcast(&ol->ol_cv);
}
mutex_exit(&ol->ol_mutex);
@@ -685,6 +692,7 @@ smb_oplock_set_grant(smb_ofile_t *of, uint8_t level)
og = &of->f_oplock_grant;
og->og_magic = SMB_OPLOCK_GRANT_MAGIC;
+ og->og_breaking = 0;
og->og_level = level;
og->og_ofile = of;
og->og_fid = of->f_fid;
@@ -782,30 +790,3 @@ smb_oplock_get_grant(smb_oplock_t *ol, smb_ofile_t *ofile)
else
return (NULL);
}
-
-/*
- * smb_oplock_create_break
- */
-static smb_oplock_break_t *
-smb_oplock_create_break(smb_node_t *node)
-{
- smb_oplock_break_t *ob;
-
- ob = kmem_cache_alloc(smb_oplock_break_cache, KM_SLEEP);
-
- smb_node_ref(node);
- ob->ob_magic = SMB_OPLOCK_BREAK_MAGIC;
- ob->ob_node = node;
-
- return (ob);
-}
-
-/*
- * smb_oplock_delete_break
- */
-static void
-smb_oplock_delete_break(smb_oplock_break_t *ob)
-{
- smb_node_release(ob->ob_node);
- kmem_cache_free(smb_oplock_break_cache, ob);
-}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_pathname.c b/usr/src/uts/common/fs/smbsrv/smb_pathname.c
index 23dc6d0e82..599698b78c 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_pathname.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_pathname.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
@@ -205,27 +205,43 @@ smb_pathname_reduce(
local_cur_node = cur_node;
local_root_node = root_node;
- if (SMB_TREE_IS_DFSROOT(sr) && (sr->smb_flg2 & SMB_FLAGS2_DFS)) {
- err = smb_pathname_dfs_preprocess(sr, usepath, SMB_MAXPATHLEN);
- if (err != 0) {
- kmem_free(usepath, SMB_MAXPATHLEN);
- return (err);
+ if (SMB_TREE_IS_DFSROOT(sr)) {
+ int is_dfs;
+ if (sr->session->dialect >= SMB_VERS_2_BASE)
+ is_dfs = sr->smb2_hdr_flags &
+ SMB2_FLAGS_DFS_OPERATIONS;
+ else
+ is_dfs = sr->smb_flg2 & SMB_FLAGS2_DFS;
+ if (is_dfs != 0) {
+ err = smb_pathname_dfs_preprocess(sr, usepath,
+ SMB_MAXPATHLEN);
+ if (err != 0) {
+ kmem_free(usepath, SMB_MAXPATHLEN);
+ return (err);
+ }
+ len = strlen(usepath);
}
- len = strlen(usepath);
}
- if (sr && (sr->smb_flg2 & SMB_FLAGS2_REPARSE_PATH)) {
- err = smb_vss_lookup_nodes(sr, root_node, cur_node,
- usepath, &vss_cur_node, &vss_root_node);
+ if (sr != NULL) {
+ boolean_t chk_vss;
+ if (sr->session->dialect >= SMB_VERS_2_BASE)
+ chk_vss = sr->arg.open.create_timewarp;
+ else
+ chk_vss = (sr->smb_flg2 &
+ SMB_FLAGS2_REPARSE_PATH) != 0;
+ if (chk_vss) {
+ err = smb_vss_lookup_nodes(sr, root_node, cur_node,
+ usepath, &vss_cur_node, &vss_root_node);
+ if (err != 0) {
+ kmem_free(usepath, SMB_MAXPATHLEN);
+ return (err);
+ }
- if (err != 0) {
- kmem_free(usepath, MAXPATHLEN);
- return (err);
+ len = strlen(usepath);
+ local_cur_node = vss_cur_node;
+ local_root_node = vss_root_node;
}
-
- len = strlen(usepath);
- local_cur_node = vss_cur_node;
- local_root_node = vss_root_node;
}
if (usepath[len - 1] == '/')
@@ -866,6 +882,8 @@ smb_pathname_strcat(smb_request_t *sr, char *s1, const char *s2)
*
* Returns: B_TRUE if pn is valid,
* otherwise returns B_FALSE and sets error status in sr.
+ *
+ * XXX: Get rid of smbsr_error calls for SMB2
*/
boolean_t
smb_pathname_validate(smb_request_t *sr, smb_pathname_t *pn)
diff --git a/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c b/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c
index 70ac2e7b24..7e9e05a163 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c
@@ -71,14 +71,7 @@
*/
#define SMB_STREAM_ENCODE_FIXED_SZ 24
-typedef struct smb_queryinfo {
- smb_node_t *qi_node; /* NULL for pipes */
- smb_attr_t qi_attr;
- boolean_t qi_delete_on_close;
- uint32_t qi_namelen;
- char qi_shortname[SMB_SHORTNAMELEN];
- char qi_name[MAXPATHLEN];
-} smb_queryinfo_t;
+/* See smb_queryinfo_t in smb_ktypes.h */
#define qi_mtime qi_attr.sa_vattr.va_mtime
#define qi_ctime qi_attr.sa_vattr.va_ctime
#define qi_atime qi_attr.sa_vattr.va_atime
@@ -95,12 +88,10 @@ static boolean_t smb_query_pipe_valid_infolev(smb_request_t *, uint16_t);
static int smb_query_encode_response(smb_request_t *, smb_xa_t *,
uint16_t, smb_queryinfo_t *);
-static void smb_encode_stream_info(smb_request_t *, smb_xa_t *,
- smb_queryinfo_t *);
-static boolean_t smb_stream_fits(smb_request_t *, smb_xa_t *, char *, uint32_t);
+static boolean_t smb_stream_fits(smb_request_t *, mbuf_chain_t *,
+ char *, uint32_t);
static int smb_query_pathname(smb_request_t *, smb_node_t *, boolean_t,
smb_queryinfo_t *);
-static void smb_query_shortname(smb_node_t *, smb_queryinfo_t *);
int smb_query_passthru;
@@ -400,12 +391,11 @@ smb_query_encode_response(smb_request_t *sr, smb_xa_t *xa,
{
uint16_t dattr;
u_offset_t datasz, allocsz;
- uint32_t isdir;
+ uint32_t status;
dattr = qinfo->qi_attr.sa_dosattr & FILE_ATTRIBUTE_MASK;
datasz = qinfo->qi_attr.sa_vattr.va_size;
allocsz = qinfo->qi_attr.sa_allocsz;
- isdir = ((dattr & FILE_ATTRIBUTE_DIRECTORY) != 0);
switch (infolev) {
case SMB_QUERY_INFORMATION:
@@ -487,7 +477,7 @@ smb_query_encode_response(smb_request_t *sr, smb_xa_t *xa,
(uint64_t)datasz,
qinfo->qi_attr.sa_vattr.va_nlink,
qinfo->qi_delete_on_close,
- (uint8_t)isdir);
+ qinfo->qi_isdir);
break;
case SMB_QUERY_FILE_EA_INFO:
@@ -520,7 +510,7 @@ smb_query_encode_response(smb_request_t *sr, smb_xa_t *xa,
(uint64_t)datasz,
qinfo->qi_attr.sa_vattr.va_nlink,
qinfo->qi_delete_on_close,
- isdir,
+ qinfo->qi_isdir,
0);
(void) smb_mbc_encodef(&xa->rep_data_mb, "%lu",
@@ -538,7 +528,9 @@ smb_query_encode_response(smb_request_t *sr, smb_xa_t *xa,
case SMB_QUERY_FILE_STREAM_INFO:
case SMB_FILE_STREAM_INFORMATION:
(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
- smb_encode_stream_info(sr, xa, qinfo);
+ status = smb_query_stream_info(sr, &xa->rep_data_mb, qinfo);
+ if (status)
+ smbsr_status(sr, status, 0, 0);
break;
case SMB_QUERY_FILE_COMPRESSION_INFO:
@@ -627,21 +619,21 @@ smb_query_encode_response(smb_request_t *sr, smb_xa_t *xa,
* a warning code is set (NT_STATUS_BUFFER_OVERFLOW). The next_offset
* value in the last returned entry must be 0.
*/
-static void
-smb_encode_stream_info(smb_request_t *sr, smb_xa_t *xa, smb_queryinfo_t *qinfo)
+uint32_t
+smb_query_stream_info(smb_request_t *sr, mbuf_chain_t *mbc,
+ smb_queryinfo_t *qinfo)
{
char *stream_name;
uint32_t next_offset;
uint32_t stream_nlen;
uint32_t pad;
u_offset_t datasz, allocsz;
- boolean_t is_dir;
smb_streaminfo_t *sinfo, *sinfo_next;
int rc = 0;
boolean_t done = B_FALSE;
boolean_t eos = B_FALSE;
- uint16_t odid;
smb_odir_t *od = NULL;
+ uint32_t status = 0;
smb_node_t *fnode = qinfo->qi_node;
smb_attr_t *attr = &qinfo->qi_attr;
@@ -656,44 +648,51 @@ smb_encode_stream_info(smb_request_t *sr, smb_xa_t *xa, smb_queryinfo_t *qinfo)
sinfo = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
sinfo_next = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
- is_dir = ((attr->sa_dosattr & FILE_ATTRIBUTE_DIRECTORY) != 0);
datasz = attr->sa_vattr.va_size;
allocsz = attr->sa_allocsz;
- odid = smb_odir_openat(sr, fnode);
- if (odid != 0)
- od = smb_tree_lookup_odir(sr, odid);
- if (od != NULL)
- rc = smb_odir_read_streaminfo(sr, od, sinfo, &eos);
-
- if ((od == NULL) || (rc != 0) || (eos))
+ status = smb_odir_openat(sr, fnode, &od);
+ switch (status) {
+ case 0:
+ break;
+ case NT_STATUS_NO_SUCH_FILE:
+ case NT_STATUS_NOT_SUPPORTED:
+ /* No streams. */
done = B_TRUE;
+ break;
+ default:
+ return (status);
+ }
+
+ if (!done) {
+ rc = smb_odir_read_streaminfo(sr, od, sinfo, &eos);
+ if ((rc != 0) || (eos))
+ done = B_TRUE;
+ }
/* If not a directory, encode an entry for the unnamed stream. */
- if (!is_dir) {
+ if (qinfo->qi_isdir == 0) {
stream_name = "::$DATA";
stream_nlen = smb_ascii_or_unicode_strlen(sr, stream_name);
next_offset = SMB_STREAM_ENCODE_FIXED_SZ + stream_nlen +
smb_ascii_or_unicode_null_len(sr);
/* Can unnamed stream fit in response buffer? */
- if (MBC_ROOM_FOR(&xa->rep_data_mb, next_offset) == 0) {
+ if (MBC_ROOM_FOR(mbc, next_offset) == 0) {
done = B_TRUE;
- smbsr_warn(sr, NT_STATUS_BUFFER_OVERFLOW,
- ERRDOS, ERROR_MORE_DATA);
+ status = NT_STATUS_BUFFER_OVERFLOW;
} else {
/* Can first named stream fit in rsp buffer? */
- if (!done && !smb_stream_fits(sr, xa, sinfo->si_name,
+ if (!done && !smb_stream_fits(sr, mbc, sinfo->si_name,
next_offset)) {
done = B_TRUE;
- smbsr_warn(sr, NT_STATUS_BUFFER_OVERFLOW,
- ERRDOS, ERROR_MORE_DATA);
+ status = NT_STATUS_BUFFER_OVERFLOW;
}
if (done)
next_offset = 0;
- (void) smb_mbc_encodef(&xa->rep_data_mb, "%llqqu", sr,
+ (void) smb_mbc_encodef(mbc, "%llqqu", sr,
next_offset, stream_nlen, datasz, allocsz,
stream_name);
}
@@ -719,11 +718,10 @@ smb_encode_stream_info(smb_request_t *sr, smb_xa_t *xa, smb_queryinfo_t *qinfo)
next_offset += pad;
/* Can next named stream fit in response buffer? */
- if (!smb_stream_fits(sr, xa, sinfo_next->si_name,
+ if (!smb_stream_fits(sr, mbc, sinfo_next->si_name,
next_offset)) {
done = B_TRUE;
- smbsr_warn(sr, NT_STATUS_BUFFER_OVERFLOW,
- ERRDOS, ERROR_MORE_DATA);
+ status = NT_STATUS_BUFFER_OVERFLOW;
}
}
@@ -732,7 +730,7 @@ smb_encode_stream_info(smb_request_t *sr, smb_xa_t *xa, smb_queryinfo_t *qinfo)
pad = 0;
}
- rc = smb_mbc_encodef(&xa->rep_data_mb, "%llqqu#.",
+ (void) smb_mbc_encodef(mbc, "%llqqu#.",
sr, next_offset, stream_nlen,
sinfo->si_size, sinfo->si_alloc_size,
sinfo->si_name, pad);
@@ -746,6 +744,8 @@ smb_encode_stream_info(smb_request_t *sr, smb_xa_t *xa, smb_queryinfo_t *qinfo)
smb_odir_close(od);
smb_odir_release(od);
}
+
+ return (status);
}
/*
@@ -761,7 +761,8 @@ smb_encode_stream_info(smb_request_t *sr, smb_xa_t *xa, smb_queryinfo_t *qinfo)
* + alignment padding
*/
static boolean_t
-smb_stream_fits(smb_request_t *sr, smb_xa_t *xa, char *name, uint32_t offset)
+smb_stream_fits(smb_request_t *sr, mbuf_chain_t *mbc,
+ char *name, uint32_t offset)
{
uint32_t len, pad;
@@ -771,7 +772,7 @@ smb_stream_fits(smb_request_t *sr, smb_xa_t *xa, char *name, uint32_t offset)
pad = smb_pad_align(len, 8);
len += pad;
- return (MBC_ROOM_FOR(&xa->rep_data_mb, offset + len) != 0);
+ return (MBC_ROOM_FOR(mbc, offset + len) != 0);
}
/*
@@ -811,6 +812,7 @@ smb_query_fileinfo(smb_request_t *sr, smb_node_t *node, uint16_t infolev,
qinfo->qi_node = node;
qinfo->qi_delete_on_close =
(node->flags & NODE_FLAGS_DELETE_ON_CLOSE) != 0;
+ qinfo->qi_isdir = smb_node_is_dir(node);
/*
* The number of links reported should be the number of
@@ -911,7 +913,7 @@ smb_query_pathname(smb_request_t *sr, smb_node_t *node, boolean_t include_share,
* using smb_mangle(), otherwise, convert the original name to
* upper-case and return it as the alternative name.
*/
-static void
+void
smb_query_shortname(smb_node_t *node, smb_queryinfo_t *qinfo)
{
char *namep;
@@ -946,6 +948,7 @@ smb_query_pipeinfo(smb_request_t *sr, smb_opipe_t *opipe, uint16_t infolev,
qinfo->qi_node = NULL;
qinfo->qi_attr.sa_vattr.va_nlink = 1;
qinfo->qi_delete_on_close = 1;
+ qinfo->qi_isdir = 0;
if ((infolev == SMB_INFO_STANDARD) ||
(infolev == SMB_INFO_QUERY_EA_SIZE) ||
diff --git a/usr/src/uts/common/fs/smbsrv/smb_quota.c b/usr/src/uts/common/fs/smbsrv/smb_quota.c
new file mode 100644
index 0000000000..cebdf41205
--- /dev/null
+++ b/usr/src/uts/common/fs/smbsrv/smb_quota.c
@@ -0,0 +1,465 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb_fsops.h>
+#include <smbsrv/smb_share.h>
+#include <smbsrv/string.h>
+#include <sys/fs/zfs.h>
+#include <smbsrv/smb_xdr.h>
+#include <smbsrv/smb_door.h>
+#include <smbsrv/smb_idmap.h>
+
+/*
+ * A user/group quota entry passed over the wire consists of:
+ * - next offset (uint32_t)
+ * - length of SID (uint32_t)
+ * - last modified time (uint64_t)
+ * - quota used (uint64_t)
+ * - quota limit (uint64_t)
+ * - quota threahold (uint64_t)
+ * - variable length sid - max = 32 bytes
+ * SMB_QUOTA_SIZE_NO_SID is the size of the above, excluding the sid.
+ */
+#define SMB_QUOTA_SIZE_NO_SID \
+ ((2 * sizeof (uint32_t)) + (4 * sizeof (uint64_t)))
+#define SMB_QUOTA_EST_SIZE (SMB_QUOTA_SIZE_NO_SID + SMB_EST_SID_SIZE)
+#define SMB_QUOTA_MAX_SIZE (SMB_QUOTA_SIZE_NO_SID + SMB_MAX_SID_SIZE)
+
+
+/*
+ * smb_quota_init_sids
+ *
+ * If the query is of type SMB_QUOTA_QUERY_SIDLIST or
+ * SMB_QUOTA_QUERY_STARTSID decode the list of sids from
+ * the client request into request->qq_sid_list.
+ * Otherwise (type SMB_QUOTA_QUERY_ALL) find the resume sid
+ * and insert it into request->qq_sid_list, or reset the
+ * resume sid to NULL if request->qq_restart.
+ *
+ * Returns: NT_STATUS codes
+ */
+uint32_t
+smb_quota_init_sids(mbuf_chain_t *mbc, smb_quota_query_t *request,
+ smb_ofile_t *ofile)
+{
+ smb_quota_sid_t *sid;
+ list_t *sid_list;
+ uint32_t status = NT_STATUS_SUCCESS;
+
+ sid_list = &request->qq_sid_list;
+ list_create(sid_list, sizeof (smb_quota_sid_t),
+ offsetof(smb_quota_sid_t, qs_list_node));
+
+ switch (request->qq_query_op) {
+ case SMB_QUOTA_QUERY_SIDLIST:
+ case SMB_QUOTA_QUERY_STARTSID:
+ status = smb_quota_decode_sids(mbc, sid_list);
+ break;
+ case SMB_QUOTA_QUERY_ALL:
+ if (request->qq_restart)
+ smb_ofile_set_quota_resume(ofile, NULL);
+ else {
+ sid = kmem_zalloc(sizeof (smb_quota_sid_t), KM_SLEEP);
+ list_insert_tail(sid_list, sid);
+ smb_ofile_get_quota_resume(ofile, sid->qs_sidstr,
+ SMB_SID_STRSZ);
+ if (*sid->qs_sidstr == '\0')
+ status = NT_STATUS_INVALID_PARAMETER;
+ }
+ break;
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ return (status);
+}
+
+/*
+ * smb_quota_free_sids
+ */
+void
+smb_quota_free_sids(smb_quota_query_t *request)
+{
+ list_t *sid_list;
+ smb_quota_sid_t *sid;
+
+ sid_list = &request->qq_sid_list;
+
+ while ((sid = list_head(sid_list)) != NULL) {
+ list_remove(sid_list, sid);
+ kmem_free(sid, sizeof (smb_quota_sid_t));
+ }
+
+ list_destroy(sid_list);
+}
+
+/*
+ * smb_quota_decode_sids
+ *
+ * Decode the SIDs from the data block and stores them in string form in list.
+ * Eaxh sid entry comprises:
+ * next_offset (4 bytes) - offset of next entry
+ * sid length (4 bytes)
+ * sid (variable length = sidlen)
+ * The last entry will have a next_offset value of 0.
+ *
+ * Returns NT_STATUS codes.
+ */
+uint32_t
+smb_quota_decode_sids(mbuf_chain_t *mbc, list_t *list)
+{
+ uint32_t offset, mb_offset, sid_offset, bytes_left;
+ uint32_t next_offset, sidlen;
+ smb_sid_t *sid;
+ smb_quota_sid_t *qsid;
+ uint32_t status = NT_STATUS_SUCCESS;
+ struct mbuf_chain sidbuf;
+ int rc;
+
+ offset = 0;
+ do {
+ mb_offset = offset + mbc->chain_offset;
+ bytes_left = mbc->max_bytes - mb_offset;
+ rc = MBC_SHADOW_CHAIN(&sidbuf, mbc,
+ mb_offset, bytes_left);
+ if (rc != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (smb_mbc_decodef(&sidbuf, "ll", &next_offset, &sidlen)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ sid_offset = offset + (2 * sizeof (uint32_t));
+ sid = smb_decode_sid(mbc, sid_offset);
+ if (sid == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ qsid = kmem_zalloc(sizeof (smb_quota_sid_t), KM_SLEEP);
+ smb_sid_tostr(sid, qsid->qs_sidstr);
+ smb_sid_free(sid);
+ sid = NULL;
+
+ list_insert_tail(list, qsid);
+ offset += next_offset;
+ } while ((next_offset != 0) && (bytes_left > 0));
+
+ return (status);
+}
+
+/*
+ * smb_quota_max_quota
+ *
+ * If the query is if type SMB_QUOTA_QUERY_SIDLIST a quota entry
+ * is returned for each sid in the sidlist. request->qr_max_quota
+ * is set to 0 and is unused.
+ * Otherwise (for SMB_QUOTA_QUERY_STARTSID and SMB_QUOTA_QUERY_ALL)
+ * max_quota is the maximum number of quota entries requested from
+ * the file system (via door call smb_quota_query()).
+ * If single is set max_quota is set to 1. If single is not set
+ * max quota is calculated as the number of quotas of size
+ * SMB_QUOTA_EST_SIZE that would fit in the response buffer.
+ */
+void
+smb_quota_max_quota(mbuf_chain_t *mbc, smb_quota_query_t *request)
+{
+ if (request->qq_query_op == SMB_QUOTA_QUERY_SIDLIST)
+ request->qq_max_quota = 0;
+ else if (request->qq_single)
+ request->qq_max_quota = 1;
+ else
+ request->qq_max_quota = (mbc->max_bytes / SMB_QUOTA_EST_SIZE);
+}
+
+/*
+ * smb_quota_decode_quotas
+ *
+ * Decode the quota entries into a list_t of smb_quota_t.
+ * SMB_QUOTA_SIZE_NO_SID is the size of a quota entry,
+ * excluding the sid.
+ * The last entry will have a next_offset value of 0.
+ *
+ * Returns NT_STATUS codes.
+ */
+uint32_t
+smb_quota_decode_quotas(mbuf_chain_t *mbc, list_t *list)
+{
+ uint32_t offset, mb_offset, sid_offset, bytes_left;
+ uint32_t next_offset, sidlen;
+ uint64_t mtime;
+ smb_sid_t *sid;
+ smb_quota_t *quota;
+ uint32_t status = NT_STATUS_SUCCESS;
+ struct mbuf_chain quotabuf;
+ int rc;
+
+ offset = 0;
+ do {
+ mb_offset = offset + mbc->chain_offset;
+ bytes_left = mbc->max_bytes - mb_offset;
+ rc = MBC_SHADOW_CHAIN(&quotabuf, mbc,
+ mb_offset, bytes_left);
+ if (rc != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ quota = kmem_zalloc(sizeof (smb_quota_t), KM_SLEEP);
+
+ if (smb_mbc_decodef(&quotabuf, "llqqqq",
+ &next_offset, &sidlen, &mtime,
+ &quota->q_used, &quota->q_thresh, &quota->q_limit)) {
+ kmem_free(quota, sizeof (smb_quota_t));
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ sid_offset = offset + SMB_QUOTA_SIZE_NO_SID;
+ sid = smb_decode_sid(mbc, sid_offset);
+ if (sid == NULL) {
+ kmem_free(quota, sizeof (smb_quota_t));
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ bzero(quota->q_sidstr, SMB_SID_STRSZ);
+ smb_sid_tostr(sid, quota->q_sidstr);
+ smb_sid_free(sid);
+ sid = NULL;
+
+ list_insert_tail(list, quota);
+ offset += next_offset;
+ } while ((next_offset != 0) && (bytes_left > 0));
+
+ return (status);
+}
+
+/*
+ * smb_quota_free_quotas
+ */
+void
+smb_quota_free_quotas(list_t *list)
+{
+ smb_quota_t *quota;
+
+ while ((quota = list_head(list)) != NULL) {
+ list_remove(list, quota);
+ kmem_free(quota, sizeof (smb_quota_t));
+ }
+
+ list_destroy(list);
+}
+
+/*
+ * smb_quota_encode_quotas
+ *
+ * Encode the quota entries from a list_t of smb_quota_t.
+ * SMB_QUOTA_SIZE_NO_SID is the size of a quota entry,
+ * excluding the sid.
+ * The last entry will have a next_offset value of 0.
+ * Sets the last encoded SID as the resume sid.
+ */
+uint32_t
+smb_quota_encode_quotas(mbuf_chain_t *mbc, smb_quota_query_t *request,
+ smb_quota_response_t *reply, smb_ofile_t *ofile)
+{
+ uint32_t next_offset, sid_offset;
+ uint64_t mtime = 0;
+ uint32_t sidlen, pad;
+ smb_sid_t *sid;
+ char *sidstr = NULL, *resume = NULL;
+ smb_quota_t *quota, *next_quota;
+ list_t *list = &reply->qr_quota_list;
+
+ int rc;
+ uint32_t status = NT_STATUS_SUCCESS;
+
+ quota = list_head(list);
+ while (quota) {
+ next_quota = list_next(list, quota);
+ sidstr = quota->q_sidstr;
+ if ((sid = smb_sid_fromstr(sidstr)) == NULL) {
+ quota = next_quota;
+ continue;
+ }
+
+ sidlen = smb_sid_len(sid);
+ sid_offset = SMB_QUOTA_SIZE_NO_SID;
+ next_offset = sid_offset + sidlen;
+ pad = smb_pad_align(next_offset, 8);
+ next_offset += pad;
+
+ if (!MBC_ROOM_FOR(mbc, next_offset)) {
+ smb_sid_free(sid);
+ break;
+ }
+ if (!MBC_ROOM_FOR(mbc,
+ next_offset + SMB_QUOTA_MAX_SIZE)) {
+ next_quota = NULL;
+ }
+
+ rc = smb_mbc_encodef(mbc, "llqqqq",
+ next_quota ? next_offset : 0, sidlen, mtime,
+ quota->q_used, quota->q_thresh, quota->q_limit);
+ if (rc == 0) {
+ smb_encode_sid(mbc, sid);
+ rc = smb_mbc_encodef(mbc, "#.", pad);
+ }
+
+ smb_sid_free(sid);
+
+ if (rc != 0) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+ resume = sidstr;
+ quota = next_quota;
+ }
+
+ if ((status == NT_STATUS_SUCCESS) &&
+ ((request->qq_query_op == SMB_QUOTA_QUERY_STARTSID) ||
+ (request->qq_query_op == SMB_QUOTA_QUERY_ALL))) {
+ smb_ofile_set_quota_resume(ofile, resume);
+ }
+
+ return (status);
+}
+
+/*
+ * smb_quota_query_user_quota
+ *
+ * Get user quota information for a single user (uid)
+ * for the current file system.
+ * Find the user's sid, insert it in the sidlist of a
+ * smb_quota_query_t request and invoke the door call
+ * smb_quota_query() to obtain the quota information.
+ *
+ * Returns: NT_STATUS codes.
+ */
+uint32_t
+smb_quota_query_user_quota(smb_request_t *sr, uid_t uid, smb_quota_t *quota)
+{
+ smb_sid_t *sid;
+ smb_quota_sid_t qsid;
+ smb_quota_query_t request;
+ smb_quota_response_t reply;
+ list_t *sid_list;
+ smb_quota_t *q;
+ smb_node_t *tnode;
+ uint32_t status = NT_STATUS_SUCCESS;
+
+ if (smb_idmap_getsid(uid, SMB_IDMAP_USER, &sid) != IDMAP_SUCCESS)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ smb_sid_tostr(sid, qsid.qs_sidstr);
+ smb_sid_free(sid);
+
+ bzero(&request, sizeof (smb_quota_query_t));
+ bzero(&reply, sizeof (smb_quota_response_t));
+
+ tnode = sr->tid_tree->t_snode;
+ request.qq_root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+ if (smb_node_getmntpath(tnode, request.qq_root_path, MAXPATHLEN) != 0) {
+ kmem_free(request.qq_root_path, MAXPATHLEN);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ sid_list = &request.qq_sid_list;
+ list_create(sid_list, sizeof (smb_quota_sid_t),
+ offsetof(smb_quota_sid_t, qs_list_node));
+ list_insert_tail(sid_list, &qsid);
+
+ request.qq_query_op = SMB_QUOTA_QUERY_SIDLIST;
+ request.qq_single = B_TRUE;
+
+ if (smb_quota_query(sr->sr_server, &request, &reply) != 0) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ } else {
+ if (reply.qr_status != NT_STATUS_SUCCESS) {
+ status = reply.qr_status;
+ } else {
+ q = list_head(&reply.qr_quota_list);
+ if ((q == NULL) ||
+ (strcmp(qsid.qs_sidstr, q->q_sidstr) != 0)) {
+ /* should never happen */
+ status = NT_STATUS_INTERNAL_ERROR;
+ } else {
+ bcopy(q, quota, sizeof (smb_quota_t));
+ }
+ }
+ xdr_free(smb_quota_response_xdr, (char *)&reply);
+ }
+
+ kmem_free(request.qq_root_path, MAXPATHLEN);
+ list_remove(sid_list, &qsid);
+ list_destroy(sid_list);
+
+ return (status);
+}
+
+/*
+ * smb_quota_query
+ *
+ * Door call to query quotas for the provided filesystem path.
+ * Returns: -1 - door call (or encode/decode) failure.
+ * 0 - success. Status set in reply.
+ */
+int
+smb_quota_query(smb_server_t *sv, smb_quota_query_t *request,
+ smb_quota_response_t *reply)
+{
+ int rc;
+
+ rc = smb_kdoor_upcall(sv, SMB_DR_QUOTA_QUERY,
+ request, smb_quota_query_xdr, reply, smb_quota_response_xdr);
+
+ return (rc);
+}
+
+/*
+ * smb_quota_set
+ *
+ * Door call to set quotas for the provided filesystem path.
+ * Returns: -1 - door call (or encode/decode) failure.
+ * 0 - success. Status set in reply.
+ */
+int
+smb_quota_set(smb_server_t *sv, smb_quota_set_t *request, uint32_t *reply)
+{
+ int rc;
+
+ rc = smb_kdoor_upcall(sv, SMB_DR_QUOTA_SET,
+ request, smb_quota_set_xdr, reply, xdr_uint32_t);
+
+ return (rc);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_rename.c b/usr/src/uts/common/fs/smbsrv/smb_rename.c
index 2ea687f1ef..1b36aa8f1e 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_rename.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_rename.c
@@ -42,20 +42,6 @@
#define SMB_NT_RENAME_MOVE_FILE 0x0105
/*
- * SMB_TRANS2_SET_FILE/PATH_INFO (RENAME_INFORMATION level) flag
- */
-#define SMB_RENAME_FLAG_OVERWRITE 0x001
-
-static int smb_common_rename(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
-static int smb_make_link(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
-static int smb_rename_check_stream(smb_fqi_t *, smb_fqi_t *);
-static int smb_rename_check_attr(smb_request_t *, smb_node_t *, uint16_t);
-static void smb_rename_set_error(smb_request_t *, int);
-
-static int smb_rename_lookup_src(smb_request_t *);
-static void smb_rename_release_src(smb_request_t *);
-
-/*
* smb_com_rename
*
* Rename a file. Files OldFileName must exist and NewFileName must not.
@@ -101,11 +87,11 @@ smb_post_rename(smb_request_t *sr)
smb_sdrc_t
smb_com_rename(smb_request_t *sr)
{
- int rc;
smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
smb_pathname_t *src_pn = &src_fqi->fq_path;
smb_pathname_t *dst_pn = &dst_fqi->fq_path;
+ uint32_t status;
if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
@@ -120,15 +106,14 @@ smb_com_rename(smb_request_t *sr)
return (SDRC_ERROR);
}
- rc = smb_common_rename(sr, src_fqi, dst_fqi);
-
- if (rc != 0) {
- smb_rename_set_error(sr, rc);
+ status = smb_common_rename(sr, src_fqi, dst_fqi);
+ if (status != 0) {
+ smbsr_error(sr, status, 0, 0);
return (SDRC_ERROR);
}
- rc = smbsr_encode_empty_result(sr);
- return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
+ (void) smbsr_encode_empty_result(sr);
+ return (SDRC_SUCCESS);
}
/*
@@ -176,11 +161,11 @@ smb_post_nt_rename(smb_request_t *sr)
smb_sdrc_t
smb_com_nt_rename(smb_request_t *sr)
{
- int rc;
smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
smb_pathname_t *src_pn = &src_fqi->fq_path;
smb_pathname_t *dst_pn = &dst_fqi->fq_path;
+ uint32_t status;
if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
@@ -203,27 +188,27 @@ smb_com_nt_rename(smb_request_t *sr)
switch (sr->arg.dirop.info_level) {
case SMB_NT_RENAME_SET_LINK_INFO:
- rc = smb_make_link(sr, src_fqi, dst_fqi);
+ status = smb_make_link(sr, src_fqi, dst_fqi);
break;
case SMB_NT_RENAME_RENAME_FILE:
case SMB_NT_RENAME_MOVE_FILE:
- rc = smb_common_rename(sr, src_fqi, dst_fqi);
+ status = smb_common_rename(sr, src_fqi, dst_fqi);
break;
case SMB_NT_RENAME_MOVE_CLUSTER_INFO:
- rc = EINVAL;
+ status = NT_STATUS_INVALID_PARAMETER;
break;
default:
- rc = EACCES;
+ status = NT_STATUS_ACCESS_DENIED;
break;
}
- if (rc != 0) {
- smb_rename_set_error(sr, rc);
+ if (status != 0) {
+ smbsr_error(sr, status, 0, 0);
return (SDRC_ERROR);
}
- rc = smbsr_encode_empty_result(sr);
- return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
+ (void) smbsr_encode_empty_result(sr);
+ return (SDRC_SUCCESS);
}
/*
@@ -247,621 +232,3 @@ smb_nt_transact_rename(smb_request_t *sr, smb_xa_t *xa)
return (SDRC_SUCCESS);
}
-
-/*
- * smb_trans2_rename
- *
- * Implements SMB_FILE_RENAME_INFORMATION level of Trans2_Set_FileInfo
- * and Trans2_Set_PathInfo.
- * If the new filename (dst_fqi) already exists it may be overwritten
- * if flags == 1.
- */
-int
-smb_trans2_rename(smb_request_t *sr, smb_node_t *node, char *fname, int flags)
-{
- int rc = 0;
- smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
- smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
- smb_pathname_t *dst_pn = &dst_fqi->fq_path;
- char *path;
- int len;
-
- sr->arg.dirop.flags = flags ? SMB_RENAME_FLAG_OVERWRITE : 0;
- sr->arg.dirop.info_level = SMB_NT_RENAME_RENAME_FILE;
-
- src_fqi->fq_sattr = SMB_SEARCH_ATTRIBUTES;
- src_fqi->fq_fnode = node;
- src_fqi->fq_dnode = node->n_dnode;
-
- /* costruct and validate the dst pathname */
- path = smb_srm_zalloc(sr, MAXPATHLEN);
- if (src_fqi->fq_path.pn_pname) {
- (void) snprintf(path, MAXPATHLEN, "%s\\%s",
- src_fqi->fq_path.pn_pname, fname);
- } else {
- rc = smb_node_getshrpath(node->n_dnode, sr->tid_tree,
- path, MAXPATHLEN);
- if (rc != 0) {
- smb_rename_set_error(sr, rc);
- return (-1);
- }
- len = strlen(path);
- (void) snprintf(path + len, MAXPATHLEN - len, "\\%s", fname);
- }
-
- smb_pathname_init(sr, dst_pn, path);
- if (!smb_pathname_validate(sr, dst_pn))
- return (-1);
-
- dst_fqi->fq_dnode = node->n_dnode;
- (void) strlcpy(dst_fqi->fq_last_comp, dst_pn->pn_fname, MAXNAMELEN);
-
- rc = smb_common_rename(sr, src_fqi, dst_fqi);
- if (rc != 0) {
- smb_rename_set_error(sr, rc);
- return (-1);
- }
-
- return (0);
-}
-
-/*
- * smb_common_rename
- *
- * Common code for renaming a file.
- *
- * If the source and destination are identical, we go through all
- * the checks but we don't actually do the rename. If the source
- * and destination files differ only in case, we do a case-sensitive
- * rename. Otherwise, we do a full case-insensitive rename.
- *
- * Returns errno values.
- */
-static int
-smb_common_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
-{
- smb_node_t *src_fnode, *src_dnode, *dst_dnode;
- smb_node_t *dst_fnode = 0;
- smb_node_t *tnode = 0;
- int rc, count;
- DWORD status;
- char *new_name, *path;
-
- path = dst_fqi->fq_path.pn_path;
-
- /* Check if attempting to rename a stream - not yet supported */
- rc = smb_rename_check_stream(src_fqi, dst_fqi);
- if (rc != 0)
- return (rc);
-
- /* The source node may already have been provided */
- if (src_fqi->fq_fnode) {
- smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
- smb_node_ref(src_fqi->fq_fnode);
- smb_node_ref(src_fqi->fq_dnode);
- } else {
- /* lookup and validate src node */
- rc = smb_rename_lookup_src(sr);
- if (rc != 0)
- return (rc);
- }
-
- src_fnode = src_fqi->fq_fnode;
- src_dnode = src_fqi->fq_dnode;
- tnode = sr->tid_tree->t_snode;
-
- /* Find destination dnode and last_comp */
- if (dst_fqi->fq_dnode) {
- smb_node_ref(dst_fqi->fq_dnode);
- } else {
- rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
- &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
- if (rc != 0) {
- smb_rename_release_src(sr);
- return (rc);
- }
- }
-
- dst_dnode = dst_fqi->fq_dnode;
- new_name = dst_fqi->fq_last_comp;
-
- /* If exact name match in same directory, we're done */
- if ((src_dnode == dst_dnode) &&
- (strcmp(src_fnode->od_name, new_name) == 0)) {
- smb_rename_release_src(sr);
- smb_node_release(dst_dnode);
- return (0);
- }
-
- /* Lookup destination node */
- rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
- dst_dnode, new_name, &dst_fqi->fq_fnode);
-
- /* If the destination node doesn't already exist, validate new_name. */
- if (rc == ENOENT) {
- if (smb_is_invalid_filename(new_name)) {
- smb_rename_release_src(sr);
- smb_node_release(dst_dnode);
- return (EILSEQ); /* NT_STATUS_OBJECT_NAME_INVALID */
- }
- }
-
- /*
- * Handle case where changing case of the same directory entry.
- *
- * If we found the dst node in the same directory as the src node,
- * and their names differ only in case:
- *
- * If the tree is case sensitive (or mixed):
- * Do case sensitive lookup to see if exact match exists.
- * If the exact match is the same node as src_node we're done.
- *
- * If the tree is case insensitive:
- * There is currently no way to tell if the case is different
- * or not, so do the rename (unless the specified new name was
- * mangled).
- */
- if ((rc == 0) &&
- (src_dnode == dst_dnode) &&
- (smb_strcasecmp(src_fnode->od_name,
- dst_fqi->fq_fnode->od_name, 0) == 0)) {
- smb_node_release(dst_fqi->fq_fnode);
- dst_fqi->fq_fnode = NULL;
-
- if (smb_tree_has_feature(sr->tid_tree,
- SMB_TREE_NO_CASESENSITIVE)) {
- if (smb_strcasecmp(src_fnode->od_name,
- dst_fqi->fq_last_comp, 0) != 0) {
- smb_rename_release_src(sr);
- smb_node_release(dst_dnode);
- return (0);
- }
- } else {
- rc = smb_fsop_lookup(sr, sr->user_cr,
- SMB_CASE_SENSITIVE, tnode, dst_dnode, new_name,
- &dst_fqi->fq_fnode);
-
- if ((rc == 0) &&
- (dst_fqi->fq_fnode == src_fnode)) {
- smb_rename_release_src(sr);
- smb_node_release(dst_fqi->fq_fnode);
- smb_node_release(dst_dnode);
- return (0);
- }
- }
- }
-
- if ((rc != 0) && (rc != ENOENT)) {
- smb_rename_release_src(sr);
- smb_node_release(dst_fqi->fq_dnode);
- return (rc);
- }
-
- if (dst_fqi->fq_fnode) {
- /*
- * Destination already exists. Do delete checks.
- */
- dst_fnode = dst_fqi->fq_fnode;
-
- if (!(sr->arg.dirop.flags && SMB_RENAME_FLAG_OVERWRITE)) {
- smb_rename_release_src(sr);
- smb_node_release(dst_fnode);
- smb_node_release(dst_dnode);
- return (EEXIST);
- }
-
- (void) smb_oplock_break(sr, dst_fnode,
- SMB_OPLOCK_BREAK_TO_NONE | SMB_OPLOCK_BREAK_BATCH);
-
- /*
- * Wait (a little) for the oplock break to be
- * responded to by clients closing handles.
- * Hold node->n_lock as reader to keep new
- * ofiles from showing up after we check.
- */
- smb_node_rdlock(dst_fnode);
- for (count = 0; count <= 12; count++) {
- status = smb_node_delete_check(dst_fnode);
- if (status != NT_STATUS_SHARING_VIOLATION)
- break;
- smb_node_unlock(dst_fnode);
- delay(MSEC_TO_TICK(100));
- smb_node_rdlock(dst_fnode);
- }
- if (status != NT_STATUS_SUCCESS) {
- smb_node_unlock(dst_fnode);
- smb_rename_release_src(sr);
- smb_node_release(dst_fnode);
- smb_node_release(dst_dnode);
- return (EACCES);
- }
-
- /*
- * Note, the combination of these two:
- * smb_node_rdlock(node);
- * nbl_start_crit(node->vp, RW_READER);
- * is equivalent to this call:
- * smb_node_start_crit(node, RW_READER)
- *
- * Cleanup after this point should use:
- * smb_node_end_crit(dst_fnode)
- */
- nbl_start_crit(dst_fnode->vp, RW_READER);
-
- /*
- * This checks nbl_share_conflict, nbl_lock_conflict
- */
- status = smb_nbl_conflict(dst_fnode, 0, UINT64_MAX, NBL_REMOVE);
- if (status != NT_STATUS_SUCCESS) {
- smb_node_end_crit(dst_fnode);
- smb_rename_release_src(sr);
- smb_node_release(dst_fnode);
- smb_node_release(dst_dnode);
- return (EACCES);
- }
-
- new_name = dst_fnode->od_name;
- }
-
- rc = smb_fsop_rename(sr, sr->user_cr,
- src_dnode, src_fnode->od_name,
- dst_dnode, new_name);
-
- if (rc == 0) {
- /*
- * Note that renames in the same directory are normally
- * delivered in {old,new} pairs, and clients expect them
- * in that order, if both events are delivered.
- */
- int a_src, a_dst; /* action codes */
- if (src_dnode == dst_dnode) {
- a_src = FILE_ACTION_RENAMED_OLD_NAME;
- a_dst = FILE_ACTION_RENAMED_NEW_NAME;
- } else {
- a_src = FILE_ACTION_REMOVED;
- a_dst = FILE_ACTION_ADDED;
- }
- smb_node_notify_change(src_dnode, a_src, src_fnode->od_name);
- smb_node_notify_change(dst_dnode, a_dst, new_name);
- }
-
- smb_rename_release_src(sr);
-
- if (dst_fqi->fq_fnode) {
- smb_node_end_crit(dst_fnode);
- smb_node_release(dst_fnode);
- }
- smb_node_release(dst_dnode);
-
- return (rc);
-}
-
-/*
- * smb_rename_check_stream
- *
- * For a stream rename the dst path must begin with ':', or "\\:".
- * We don't yet support stream rename, Return EACCES.
- *
- * If not a stream rename, in accordance with the above rule,
- * it is not valid for either the src or dst to be a stream.
- * Return EINVAL.
- */
-static int
-smb_rename_check_stream(smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
-{
- smb_node_t *src_fnode = src_fqi->fq_fnode;
- char *src_path = src_fqi->fq_path.pn_path;
- char *dst_path = dst_fqi->fq_path.pn_path;
-
- /* We do not yet support named stream rename - ACCESS DENIED */
- if ((dst_path[0] == ':') ||
- ((dst_path[0] == '\\') && (dst_path[1] == ':'))) {
- return (EACCES);
- }
-
- /*
- * If not stream rename (above) neither src or dst can be
- * a named stream.
- */
-
- if (smb_is_stream_name(dst_path))
- return (EINVAL);
-
- if (src_fqi->fq_fnode) {
- if (SMB_IS_STREAM(src_fnode))
- return (EINVAL);
- } else {
- if (smb_is_stream_name(src_path))
- return (EINVAL);
- }
-
- return (0);
-}
-
-
-/*
- * smb_make_link
- *
- * Creating a hard link (adding an additional name) for a file.
- *
- * If the source and destination are identical, we go through all
- * the checks but we don't create a link.
- *
- * If the file is a symlink we create the hardlink on the target
- * of the symlink (i.e. use SMB_FOLLOW_LINKS when looking up src).
- * If the target of the symlink does not exist we fail with ENOENT.
- *
- * Returns errno values.
- */
-static int
-smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
-{
- smb_node_t *tnode;
- char *path;
- int rc;
-
- /* Cannnot create link on named stream */
- if (smb_is_stream_name(src_fqi->fq_path.pn_path) ||
- smb_is_stream_name(dst_fqi->fq_path.pn_path)) {
- return (EINVAL);
- }
-
- /* lookup and validate src node */
- rc = smb_rename_lookup_src(sr);
- if (rc != 0)
- return (rc);
-
- /* if src and dest paths match we're done */
- if (smb_strcasecmp(src_fqi->fq_path.pn_path,
- dst_fqi->fq_path.pn_path, 0) == 0) {
- smb_rename_release_src(sr);
- return (0);
- }
-
- /* find the destination dnode and last_comp */
- tnode = sr->tid_tree->t_snode;
- path = dst_fqi->fq_path.pn_path;
- rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
- &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
- if (rc != 0) {
- smb_rename_release_src(sr);
- return (rc);
- }
-
- /* If name match in same directory, we're done */
- if ((src_fqi->fq_dnode == dst_fqi->fq_dnode) &&
- (smb_strcasecmp(src_fqi->fq_fnode->od_name,
- dst_fqi->fq_last_comp, 0) == 0)) {
- smb_rename_release_src(sr);
- smb_node_release(dst_fqi->fq_dnode);
- return (0);
- }
-
- if (smb_is_invalid_filename(dst_fqi->fq_last_comp)) {
- smb_rename_release_src(sr);
- smb_node_release(dst_fqi->fq_dnode);
- return (EILSEQ); /* NT_STATUS_INVALID_OBJECT_NAME */
- }
-
- /* Lookup the destination node. It MUST NOT exist. */
- rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
- dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode);
- if (rc == 0) {
- smb_node_release(dst_fqi->fq_fnode);
- rc = EEXIST;
- }
- if (rc != ENOENT) {
- smb_rename_release_src(sr);
- smb_node_release(dst_fqi->fq_dnode);
- return (rc);
- }
-
- rc = smb_fsop_link(sr, sr->user_cr, src_fqi->fq_fnode,
- dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
-
- if (rc == 0) {
- smb_node_notify_change(dst_fqi->fq_dnode,
- FILE_ACTION_ADDED, dst_fqi->fq_last_comp);
- }
-
- smb_rename_release_src(sr);
- smb_node_release(dst_fqi->fq_dnode);
- return (rc);
-}
-
-/*
- * smb_rename_lookup_src
- *
- * Lookup the src node, checking for sharing violations and
- * breaking any existing BATCH oplock.
- * Populate sr->arg.dirop.fqi
- *
- * Upon success, the dnode and fnode will have holds and the
- * fnode will be in a critical section. These should be
- * released using smb_rename_release_src().
- *
- * Returns errno values.
- */
-static int
-smb_rename_lookup_src(smb_request_t *sr)
-{
- smb_node_t *src_node, *tnode;
- DWORD status;
- int rc;
- int count;
- char *path;
-
- struct dirop *dirop = &sr->arg.dirop;
- smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
-
- if (smb_is_stream_name(src_fqi->fq_path.pn_path))
- return (EINVAL);
-
- /* Lookup the source node */
- tnode = sr->tid_tree->t_snode;
- path = src_fqi->fq_path.pn_path;
- rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
- &src_fqi->fq_dnode, src_fqi->fq_last_comp);
- if (rc != 0)
- return (rc);
-
- rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
- src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode);
- if (rc != 0) {
- smb_node_release(src_fqi->fq_dnode);
- return (rc);
- }
-
- /* Not valid to create hardlink for directory */
- if ((dirop->info_level == SMB_NT_RENAME_SET_LINK_INFO) &&
- (smb_node_is_dir(src_fqi->fq_fnode))) {
- smb_node_release(src_fqi->fq_fnode);
- smb_node_release(src_fqi->fq_dnode);
- return (EISDIR);
- }
-
- src_node = src_fqi->fq_fnode;
-
- rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr);
- if (rc != 0) {
- smb_node_release(src_fqi->fq_fnode);
- smb_node_release(src_fqi->fq_dnode);
- return (rc);
- }
-
- /*
- * Break BATCH oplock before ofile checks. If a client
- * has a file open, this will force a flush or close,
- * which may affect the outcome of any share checking.
- */
- (void) smb_oplock_break(sr, src_node,
- SMB_OPLOCK_BREAK_TO_LEVEL_II | SMB_OPLOCK_BREAK_BATCH);
-
- /*
- * Wait (a little) for the oplock break to be
- * responded to by clients closing handles.
- * Hold node->n_lock as reader to keep new
- * ofiles from showing up after we check.
- */
- smb_node_rdlock(src_node);
- for (count = 0; count <= 12; count++) {
- status = smb_node_rename_check(src_node);
- if (status != NT_STATUS_SHARING_VIOLATION)
- break;
- smb_node_unlock(src_node);
- delay(MSEC_TO_TICK(100));
- smb_node_rdlock(src_node);
- }
- if (status != NT_STATUS_SUCCESS) {
- smb_node_unlock(src_node);
- smb_node_release(src_fqi->fq_fnode);
- smb_node_release(src_fqi->fq_dnode);
- return (EPIPE); /* = ERRbadshare */
- }
-
- /*
- * Note, the combination of these two:
- * smb_node_rdlock(node);
- * nbl_start_crit(node->vp, RW_READER);
- * is equivalent to this call:
- * smb_node_start_crit(node, RW_READER)
- *
- * Cleanup after this point should use:
- * smb_node_end_crit(src_node)
- */
- nbl_start_crit(src_node->vp, RW_READER);
-
- /*
- * This checks nbl_share_conflict, nbl_lock_conflict
- */
- status = smb_nbl_conflict(src_node, 0, UINT64_MAX, NBL_RENAME);
- if (status != NT_STATUS_SUCCESS) {
- smb_node_end_crit(src_node);
- smb_node_release(src_fqi->fq_fnode);
- smb_node_release(src_fqi->fq_dnode);
- if (status == NT_STATUS_SHARING_VIOLATION)
- return (EPIPE); /* = ERRbadshare */
- return (EACCES);
- }
-
- /* NB: Caller expects holds on src_fqi fnode, dnode */
- return (0);
-}
-
-/*
- * smb_rename_release_src
- */
-static void
-smb_rename_release_src(smb_request_t *sr)
-{
- smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
-
- smb_node_end_crit(src_fqi->fq_fnode);
- smb_node_release(src_fqi->fq_fnode);
- smb_node_release(src_fqi->fq_dnode);
-}
-
-
-static int
-smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr)
-{
- smb_attr_t attr;
-
- bzero(&attr, sizeof (attr));
- attr.sa_mask = SMB_AT_DOSATTR;
- if (smb_node_getattr(sr, node, zone_kcred(), NULL, &attr) != 0)
- return (EACCES);
-
- if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) &&
- !(SMB_SEARCH_HIDDEN(sattr)))
- return (ESRCH);
-
- if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) &&
- !(SMB_SEARCH_SYSTEM(sattr)))
- return (ESRCH);
-
- return (0);
-}
-
-/*
- * The following values are based on observed WFWG, Windows 9x, Windows NT
- * and Windows 2000 behaviour.
- *
- * ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
- *
- * Windows 95 clients don't see the problem because the target is deleted
- * before the rename request.
- */
-static void
-smb_rename_set_error(smb_request_t *sr, int errnum)
-{
- static struct {
- int errnum;
- uint16_t errcode;
- uint32_t status32;
- } rc_map[] = {
- { EEXIST, ERROR_ALREADY_EXISTS, NT_STATUS_OBJECT_NAME_COLLISION },
- { EPIPE, ERROR_SHARING_VIOLATION, NT_STATUS_SHARING_VIOLATION },
- { ENOENT, ERROR_FILE_NOT_FOUND, NT_STATUS_OBJECT_NAME_NOT_FOUND },
- { ESRCH, ERROR_FILE_NOT_FOUND, NT_STATUS_NO_SUCH_FILE },
- { EINVAL, ERROR_INVALID_PARAMETER, NT_STATUS_INVALID_PARAMETER },
- { EACCES, ERROR_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED },
- { EISDIR, ERROR_ACCESS_DENIED, NT_STATUS_FILE_IS_A_DIRECTORY },
- { EIO, ERROR_INTERNAL_ERROR, NT_STATUS_INTERNAL_ERROR }
- };
-
- int i;
-
- if (errnum == 0)
- return;
-
- for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) {
- if (rc_map[i].errnum == errnum) {
- smbsr_error(sr, rc_map[i].status32,
- ERRDOS, rc_map[i].errcode);
- return;
- }
- }
-
- smbsr_errno(sr, errnum);
-}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_sd.c b/usr/src/uts/common/fs/smbsrv/smb_sd.c
index 12c98fdbdb..ddbd7b9413 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_sd.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_sd.c
@@ -21,6 +21,8 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -125,7 +127,6 @@ uint32_t
smb_sd_read(smb_request_t *sr, smb_sd_t *sd, uint32_t secinfo)
{
smb_fssd_t fs_sd;
- smb_error_t smb_err;
smb_node_t *node;
uint32_t status = NT_STATUS_SUCCESS;
uint32_t sd_flags;
@@ -136,10 +137,8 @@ smb_sd_read(smb_request_t *sr, smb_sd_t *sd, uint32_t secinfo)
smb_fssd_init(&fs_sd, secinfo, sd_flags);
error = smb_fsop_sdread(sr, sr->user_cr, node, &fs_sd);
- if (error) {
- smbsr_map_errno(error, &smb_err);
- return (smb_err.status);
- }
+ if (error)
+ return (smb_errno2status(error));
status = smb_sd_fromfs(&fs_sd, sd);
smb_fssd_term(&fs_sd);
@@ -159,7 +158,6 @@ smb_sd_write(smb_request_t *sr, smb_sd_t *sd, uint32_t secinfo)
{
smb_node_t *node;
smb_fssd_t fs_sd;
- smb_error_t smb_err;
uint32_t status;
uint32_t sd_flags;
int error;
@@ -180,8 +178,7 @@ smb_sd_write(smb_request_t *sr, smb_sd_t *sd, uint32_t secinfo)
if (error) {
if (error == EBADE)
return (NT_STATUS_INVALID_OWNER);
- smbsr_map_errno(error, &smb_err);
- return (smb_err.status);
+ return (smb_errno2status(error));
}
return (NT_STATUS_SUCCESS);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_server.c b/usr/src/uts/common/fs/smbsrv/smb_server.c
index e4a305c7fa..176a35aae7 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 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -210,7 +210,7 @@
#include <netinet/ip_icmp.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
-#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb2_kproto.h>
#include <smbsrv/string.h>
#include <smbsrv/netbios.h>
#include <smbsrv/smb_fsops.h>
@@ -280,6 +280,15 @@ kmem_cache_t *smb_cache_event;
*/
/*
+ * How many zones have an SMB server active?
+ */
+int
+smb_server_get_count(void)
+{
+ return (smb_llist_get_count(&smb_servers));
+}
+
+/*
* smb_server_g_init
*
* This function must be called from smb_drv_attach().
@@ -293,13 +302,10 @@ smb_server_g_init(void)
goto errout;
if ((rc = smb_fem_init()) != 0)
goto errout;
- if ((rc = smb_oplock_init()) != 0)
- goto errout;
smb_kshare_g_init();
smb_codepage_init();
smb_mbc_init(); /* smb_mbc_cache */
- smb_net_init(); /* smb_txr_cache */
smb_node_init(); /* smb_node_cache, lists */
smb_cache_request = kmem_cache_create("smb_request_cache",
@@ -337,12 +343,12 @@ errout:
* This function must called from smb_drv_detach(). It will fail if servers
* still exist.
*/
-int
+void
smb_server_g_fini(void)
{
- if (smb_llist_get_count(&smb_servers) != 0)
- return (EBUSY);
+ ASSERT(smb_llist_get_count(&smb_servers) == 0);
+
smb_llist_fini();
kmem_cache_destroy(smb_cache_request);
@@ -355,17 +361,13 @@ smb_server_g_fini(void)
kmem_cache_destroy(smb_cache_event);
smb_node_fini();
- smb_net_fini();
smb_mbc_fini();
smb_kshare_g_fini();
- smb_oplock_fini();
smb_fem_fini();
smb_vop_fini();
smb_llist_destructor(&smb_servers);
-
- return (0);
}
/*
@@ -413,7 +415,10 @@ smb_server_create(void)
smb_llist_constructor(&sv->sp_info.sp_fidlist,
sizeof (smb_spoolfid_t), offsetof(smb_spoolfid_t, sf_lnd));
- sv->sv_disp_stats = kmem_zalloc(SMB_COM_NUM *
+ sv->sv_disp_stats1 = kmem_zalloc(SMB_COM_NUM *
+ sizeof (smb_disp_stats_t), KM_SLEEP);
+
+ sv->sv_disp_stats2 = kmem_zalloc(SMB2__NCMDS *
sizeof (smb_disp_stats_t), KM_SLEEP);
smb_thread_init(&sv->si_thread_timers, "smb_timers",
@@ -502,9 +507,12 @@ smb_server_delete(void)
smb_kdoor_fini(sv);
smb_llist_destructor(&sv->sv_event_list);
- kmem_free(sv->sv_disp_stats,
+ kmem_free(sv->sv_disp_stats1,
SMB_COM_NUM * sizeof (smb_disp_stats_t));
+ kmem_free(sv->sv_disp_stats2,
+ SMB2__NCMDS * sizeof (smb_disp_stats_t));
+
smb_srqueue_destroy(&sv->sv_srqueue);
smb_thread_destroy(&sv->si_thread_timers);
@@ -1068,7 +1076,6 @@ 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:
- case SMB_SESSION_STATE_OPLOCK_BREAKING:
smb_session_disconnect_share(session, sharename);
break;
default:
@@ -1245,6 +1252,7 @@ smb_server_kstat_init(smb_server_t *sv)
((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_start_time =
sv->sv_start_time;
smb_dispatch_stats_init(sv);
+ smb2_dispatch_stats_init(sv);
kstat_install(sv->sv_ksp);
} else {
cmn_err(CE_WARN, "SMB Server: Statistics unavailable");
@@ -1295,6 +1303,7 @@ smb_server_kstat_fini(smb_server_t *sv)
kstat_delete(sv->sv_ksp);
sv->sv_ksp = NULL;
smb_dispatch_stats_fini(sv);
+ smb2_dispatch_stats_fini(sv);
}
}
@@ -1335,7 +1344,8 @@ smb_server_kstat_update(kstat_t *ksp, int rw)
/*
* Latency & Throughput of the requests
*/
- smb_dispatch_stats_update(sv, ksd->ks_reqs, 0, SMB_COM_NUM);
+ smb_dispatch_stats_update(sv, ksd->ks_reqs1, 0, SMB_COM_NUM);
+ smb2_dispatch_stats_update(sv, ksd->ks_reqs2, 0, SMB2__NCMDS);
return (0);
}
if (rw == KSTAT_WRITE)
@@ -1914,9 +1924,13 @@ smb_server_store_cfg(smb_server_t *sv, smb_ioc_cfg_t *ioc)
sv->sv_cfg.skc_ipv6_enable = ioc->ipv6_enable;
sv->sv_cfg.skc_print_enable = ioc->print_enable;
sv->sv_cfg.skc_traverse_mounts = ioc->traverse_mounts;
+ sv->sv_cfg.skc_max_protocol = ioc->max_protocol;
sv->sv_cfg.skc_execflags = ioc->exec_flags;
sv->sv_cfg.skc_negtok_len = ioc->negtok_len;
sv->sv_cfg.skc_version = ioc->version;
+ sv->sv_cfg.skc_initial_credits = ioc->initial_credits;
+ sv->sv_cfg.skc_maximum_credits = ioc->maximum_credits;
+
(void) memcpy(sv->sv_cfg.skc_machine_uuid, ioc->machine_uuid,
sizeof (uuid_t));
(void) memcpy(sv->sv_cfg.skc_negtok, ioc->negtok,
diff --git a/usr/src/uts/common/fs/smbsrv/smb_session.c b/usr/src/uts/common/fs/smbsrv/smb_session.c
index fc130cb973..3caa759caa 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_session.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_session.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 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/atomic.h>
@@ -29,10 +29,13 @@
#include <sys/sdt.h>
#include <sys/random.h>
#include <smbsrv/netbios.h>
-#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb2_kproto.h>
#include <smbsrv/string.h>
#include <netinet/tcp.h>
+/* How many iovec we'll handle as a local array (no allocation) */
+#define SMB_LOCAL_IOV_MAX 16
+
#define SMB_NEW_KID() atomic_inc_64_nv(&smb_kids)
static volatile uint64_t smb_kids;
@@ -43,10 +46,13 @@ static volatile uint64_t smb_kids;
*/
uint32_t smb_keep_alive = SMB_PI_KEEP_ALIVE_MIN / 60;
+static int smbsr_newrq_initial(smb_request_t *);
+
static void smb_session_cancel(smb_session_t *);
-static int smb_session_message(smb_session_t *);
-static int smb_session_xprt_puthdr(smb_session_t *, smb_xprt_t *,
- uint8_t *, size_t);
+static int smb_session_reader(smb_session_t *);
+static int smb_session_xprt_puthdr(smb_session_t *,
+ uint8_t msg_type, uint32_t msg_len,
+ 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_request_init_command_mbuf(smb_request_t *sr);
@@ -108,58 +114,95 @@ smb_session_correct_keep_alive_values(smb_llist_t *ll, uint32_t new_keep_alive)
/*
* Send a session message - supports SMB-over-NBT and SMB-over-TCP.
+ * If an mbuf chain is provided (optional), it will be freed and
+ * set to NULL -- unconditionally! (error or not)
*
- * The mbuf chain is copied into a contiguous buffer so that the whole
- * message is submitted to smb_sosend as a single request. This should
- * help Ethereal/Wireshark delineate the packets correctly even though
- * TCP_NODELAY has been set on the socket.
- *
- * If an mbuf chain is provided, it will be freed and set to NULL here.
+ * Builds a I/O vector (uio/iov) to do the send from mbufs, plus one
+ * segment for the 4-byte NBT header.
*/
int
-smb_session_send(smb_session_t *session, uint8_t type, mbuf_chain_t *mbc)
+smb_session_send(smb_session_t *session, uint8_t nbt_type, mbuf_chain_t *mbc)
{
- smb_txreq_t *txr;
- smb_xprt_t hdr;
+ uio_t uio;
+ iovec_t local_iov[SMB_LOCAL_IOV_MAX];
+ iovec_t *alloc_iov = NULL;
+ int alloc_sz = 0;
+ mbuf_t *m;
+ uint8_t nbt_hdr[NETBIOS_HDR_SZ];
+ uint32_t nbt_len;
+ int i, nseg;
int rc;
switch (session->s_state) {
case SMB_SESSION_STATE_DISCONNECTED:
case SMB_SESSION_STATE_TERMINATED:
- if ((mbc != NULL) && (mbc->chain != NULL)) {
- m_freem(mbc->chain);
- mbc->chain = NULL;
- mbc->flags = 0;
- }
- return (ENOTCONN);
+ rc = ENOTCONN;
+ goto out;
default:
break;
}
- txr = smb_net_txr_alloc();
-
- if ((mbc != NULL) && (mbc->chain != NULL)) {
- rc = mbc_moveout(mbc, (caddr_t)&txr->tr_buf[NETBIOS_HDR_SZ],
- sizeof (txr->tr_buf) - NETBIOS_HDR_SZ, &txr->tr_len);
- if (rc != 0) {
- smb_net_txr_free(txr);
- return (rc);
- }
+ /*
+ * Setup the IOV. First, count the number of IOV segments
+ * (plus one for the NBT header) and decide whether we
+ * need to allocate an iovec or can use local_iov;
+ */
+ bzero(&uio, sizeof (uio));
+ nseg = 1;
+ m = (mbc != NULL) ? mbc->chain : NULL;
+ while (m != NULL) {
+ nseg++;
+ m = m->m_next;
}
+ if (nseg <= SMB_LOCAL_IOV_MAX) {
+ uio.uio_iov = local_iov;
+ } else {
+ alloc_sz = nseg * sizeof (iovec_t);
+ alloc_iov = kmem_alloc(alloc_sz, KM_SLEEP);
+ uio.uio_iov = alloc_iov;
+ }
+ uio.uio_iovcnt = nseg;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_extflg = UIO_COPY_DEFAULT;
- hdr.xh_type = type;
- hdr.xh_length = (uint32_t)txr->tr_len;
-
- rc = smb_session_xprt_puthdr(session, &hdr, txr->tr_buf,
- NETBIOS_HDR_SZ);
+ /*
+ * Build the iov list, meanwhile computing the length of
+ * the SMB payload (to put in the NBT header).
+ */
+ uio.uio_iov[0].iov_base = (void *)nbt_hdr;
+ uio.uio_iov[0].iov_len = sizeof (nbt_hdr);
+ i = 1;
+ nbt_len = 0;
+ m = (mbc != NULL) ? mbc->chain : NULL;
+ while (m != NULL) {
+ uio.uio_iov[i].iov_base = m->m_data;
+ uio.uio_iov[i++].iov_len = m->m_len;
+ nbt_len += m->m_len;
+ m = m->m_next;
+ }
+ ASSERT3S(i, ==, nseg);
- if (rc != 0) {
- smb_net_txr_free(txr);
- return (rc);
+ /*
+ * Set the NBT header, set uio_resid
+ */
+ uio.uio_resid = nbt_len + NETBIOS_HDR_SZ;
+ rc = smb_session_xprt_puthdr(session, nbt_type, nbt_len,
+ nbt_hdr, NETBIOS_HDR_SZ);
+ if (rc != 0)
+ goto out;
+
+ smb_server_add_txb(session->s_server, (int64_t)uio.uio_resid);
+ rc = smb_net_send_uio(session, &uio);
+
+out:
+ if (alloc_iov != NULL)
+ kmem_free(alloc_iov, alloc_sz);
+ if ((mbc != NULL) && (mbc->chain != NULL)) {
+ m_freem(mbc->chain);
+ mbc->chain = NULL;
+ mbc->flags = 0;
}
- txr->tr_len += NETBIOS_HDR_SZ;
- smb_server_add_txb(session->s_server, (int64_t)txr->tr_len);
- return (smb_net_txr_send(session->sock, &session->s_txlst, txr));
+ return (rc);
}
/*
@@ -175,7 +218,7 @@ smb_session_send(smb_session_t *session, uint8_t type, mbuf_chain_t *mbc)
* if the client is behind a NAT server.
*/
static int
-smb_session_request(struct smb_session *session)
+smb_netbios_session_request(struct smb_session *session)
{
int rc;
char *calling_name;
@@ -308,30 +351,40 @@ smb_session_xprt_gethdr(smb_session_t *session, smb_xprt_t *ret_hdr)
/*
* Encode a transport session packet header into a 4-byte buffer.
- * See smb_xprt_t definition for header format information.
*/
static int
-smb_session_xprt_puthdr(smb_session_t *session, smb_xprt_t *hdr,
+smb_session_xprt_puthdr(smb_session_t *session,
+ uint8_t msg_type, uint32_t msg_length,
uint8_t *buf, size_t buflen)
{
- if (session == NULL || hdr == NULL ||
- buf == NULL || buflen < NETBIOS_HDR_SZ) {
+ if (buf == NULL || buflen < NETBIOS_HDR_SZ) {
return (-1);
}
switch (session->s_local_port) {
case IPPORT_NETBIOS_SSN:
- buf[0] = hdr->xh_type;
- buf[1] = ((hdr->xh_length >> 16) & 1);
- buf[2] = (hdr->xh_length >> 8) & 0xff;
- buf[3] = hdr->xh_length & 0xff;
+ /* Per RFC 1001, 1002: msg. len < 128KB */
+ if (msg_length >= (1 << 17))
+ return (-1);
+ buf[0] = msg_type;
+ buf[1] = ((msg_length >> 16) & 1);
+ buf[2] = (msg_length >> 8) & 0xff;
+ buf[3] = msg_length & 0xff;
break;
case IPPORT_SMB:
- buf[0] = hdr->xh_type;
- buf[1] = (hdr->xh_length >> 16) & 0xff;
- buf[2] = (hdr->xh_length >> 8) & 0xff;
- buf[3] = hdr->xh_length & 0xff;
+ /*
+ * SMB over TCP is like NetBIOS but the one byte
+ * message type is always zero, and the length
+ * part is three bytes. It could actually use
+ * longer messages, but this is conservative.
+ */
+ if (msg_length >= (1 << 24))
+ return (-1);
+ buf[0] = msg_type;
+ buf[1] = (msg_length >> 16) & 0xff;
+ buf[2] = (msg_length >> 8) & 0xff;
+ buf[3] = msg_length & 0xff;
break;
default:
@@ -429,7 +482,7 @@ smb_session_receiver(smb_session_t *session)
session->s_thread = curthread;
if (session->s_local_port == IPPORT_NETBIOS_SSN) {
- rc = smb_session_request(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;
@@ -442,7 +495,7 @@ smb_session_receiver(smb_session_t *session)
session->s_state = SMB_SESSION_STATE_ESTABLISHED;
smb_rwx_rwexit(&session->s_lock);
- (void) smb_session_message(session);
+ (void) smb_session_reader(session);
smb_rwx_rwenter(&session->s_lock, RW_WRITER);
session->s_state = SMB_SESSION_STATE_DISCONNECTED;
@@ -476,7 +529,6 @@ smb_session_disconnect(smb_session_t *session)
case SMB_SESSION_STATE_CONNECTED:
case SMB_SESSION_STATE_ESTABLISHED:
case SMB_SESSION_STATE_NEGOTIATED:
- case SMB_SESSION_STATE_OPLOCK_BREAKING:
smb_soshutdown(session->sock);
session->s_state = SMB_SESSION_STATE_DISCONNECTED;
_NOTE(FALLTHRU)
@@ -500,7 +552,7 @@ smb_session_disconnect(smb_session_t *session)
* 6 Unable to read SMB data
*/
static int
-smb_session_message(smb_session_t *session)
+smb_session_reader(smb_session_t *session)
{
smb_server_t *sv;
smb_request_t *sr = NULL;
@@ -535,44 +587,38 @@ smb_session_message(smb_session_t *session)
return (EPROTO);
}
+ if (hdr.xh_length == 0) {
+ /* zero length is another form of keep alive */
+ session->keep_alive = smb_keep_alive;
+ continue;
+ }
+
if (hdr.xh_length < SMB_HEADER_LEN)
return (EPROTO);
+ if (hdr.xh_length > session->cmd_max_bytes)
+ return (EPROTO);
session->keep_alive = smb_keep_alive;
+
/*
- * Allocate a request context, read the SMB header and validate
- * it. The sr includes a buffer large enough to hold the SMB
- * request payload. If the header looks valid, read any
- * remaining data.
+ * Allocate a request context, read the whole message.
*/
sr = smb_request_alloc(session, hdr.xh_length);
req_buf = (uint8_t *)sr->sr_request_buf;
resid = hdr.xh_length;
- rc = smb_sorecv(session->sock, req_buf, SMB_HEADER_LEN);
+ rc = smb_sorecv(session->sock, req_buf, resid);
if (rc) {
smb_request_free(sr);
- return (rc);
- }
-
- if (SMB_PROTOCOL_MAGIC_INVALID(sr)) {
- smb_request_free(sr);
- return (EPROTO);
+ break;
}
- if (resid > SMB_HEADER_LEN) {
- req_buf += SMB_HEADER_LEN;
- resid -= SMB_HEADER_LEN;
-
- rc = smb_sorecv(session->sock, req_buf, resid);
- if (rc) {
- smb_request_free(sr);
- return (rc);
- }
- }
+ /* accounting: requests, received bytes */
+ smb_server_inc_req(sv);
smb_server_add_rxb(sv,
(int64_t)(hdr.xh_length + NETBIOS_HDR_SZ));
+
/*
* Initialize command MBC to represent the received data.
*/
@@ -580,23 +626,52 @@ smb_session_message(smb_session_t *session)
DTRACE_PROBE1(session__receive__smb, smb_request_t *, sr);
- if (sr->session->signing.flags & SMB_SIGNING_ENABLED) {
- if (SMB_IS_NT_CANCEL(sr)) {
- sr->session->signing.seqnum++;
- sr->sr_seqnum = sr->session->signing.seqnum + 1;
- sr->reply_seqnum = 0;
- } else {
- sr->session->signing.seqnum += 2;
- sr->sr_seqnum = sr->session->signing.seqnum;
- sr->reply_seqnum = sr->sr_seqnum + 1;
- }
- }
- sr->sr_time_submitted = gethrtime();
- sr->sr_state = SMB_REQ_STATE_SUBMITTED;
- smb_srqueue_waitq_enter(session->s_srqueue);
- (void) taskq_dispatch(session->s_server->sv_worker_pool,
- smb_session_worker, sr, TQ_SLEEP);
+ rc = session->newrq_func(sr);
+ sr = NULL; /* enqueued or freed */
+ if (rc != 0)
+ break;
}
+ return (rc);
+}
+
+/*
+ * This is the initial handler for new smb requests, called from
+ * from smb_session_reader when we have not yet seen any requests.
+ * The first SMB request must be "negotiate", which determines
+ * which protocol and dialect we'll be using. That's the ONLY
+ * request type handled here, because with all later requests,
+ * we know the protocol and handle those with either the SMB1 or
+ * SMB2 handlers: smb1sr_post() or smb2sr_post().
+ * Those do NOT allow SMB negotiate, because that's only allowed
+ * as the first request on new session.
+ *
+ * This and other "post a request" handlers must either enqueue
+ * the new request for the session taskq, or smb_request_free it
+ * (in case we've decided to drop this connection). In this
+ * (special) new request handler, we always free the request.
+ */
+static int
+smbsr_newrq_initial(smb_request_t *sr)
+{
+ uint32_t magic;
+ int rc = EPROTO;
+
+ mutex_enter(&sr->sr_mutex);
+ sr->sr_state = SMB_REQ_STATE_ACTIVE;
+ mutex_exit(&sr->sr_mutex);
+
+ magic = SMB_READ_PROTOCOL(sr->sr_request_buf);
+ if (magic == SMB_PROTOCOL_MAGIC)
+ rc = smb1_newrq_negotiate(sr);
+ if (magic == SMB2_PROTOCOL_MAGIC)
+ rc = smb2_newrq_negotiate(sr);
+
+ mutex_enter(&sr->sr_mutex);
+ sr->sr_state = SMB_REQ_STATE_COMPLETED;
+ mutex_exit(&sr->sr_mutex);
+
+ smb_request_free(sr);
+ return (rc);
}
/*
@@ -611,6 +686,7 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
struct sockaddr_in6 sin6;
smb_session_t *session;
int64_t now;
+ uint16_t rport;
session = kmem_cache_alloc(smb_cache_session, KM_SLEEP);
bzero(session, sizeof (smb_session_t));
@@ -636,6 +712,8 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
smb_session_genkey(session);
+ mutex_init(&session->s_credits_mutex, NULL, MUTEX_DEFAULT, NULL);
+
smb_slist_constructor(&session->s_req_list, sizeof (smb_request_t),
offsetof(smb_request_t, sr_session_lnd));
@@ -666,6 +744,7 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
bcopy(&sin.sin_addr,
&session->ipaddr.au_addr.au_ipv4,
sizeof (in_addr_t));
+ rport = sin.sin_port;
} else {
slen = sizeof (sin6);
(void) ksocket_getsockname(new_so,
@@ -679,10 +758,12 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
bcopy(&sin6.sin6_addr,
&session->ipaddr.au_addr.au_ipv6,
sizeof (in6_addr_t));
+ rport = sin6.sin6_port;
}
session->ipaddr.a_family = family;
session->local_ipaddr.a_family = family;
session->s_local_port = port;
+ session->s_remote_port = ntohs(rport);
session->sock = new_so;
(void) smb_inet_ntop(&session->ipaddr,
session->ip_addr_str, INET6_ADDRSTRLEN);
@@ -695,6 +776,16 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
smb_server_get_cfg(sv, &session->s_cfg);
session->s_srqueue = &sv->sv_srqueue;
+ /*
+ * The initial new request handler is special,
+ * and only accepts negotiation requests.
+ */
+ session->newrq_func = smbsr_newrq_initial;
+
+ /* These may increase in SMB2 negotiate. */
+ session->cmd_max_bytes = SMB_REQ_MAX_SIZE;
+ session->reply_max_bytes = SMB_REQ_MAX_SIZE;
+
session->s_magic = SMB_SESSION_MAGIC;
return (session);
}
@@ -718,6 +809,8 @@ smb_session_delete(smb_session_t *session)
smb_rwx_destroy(&session->s_lock);
smb_net_txl_destructor(&session->s_txlst);
+ mutex_destroy(&session->s_credits_mutex);
+
smb_slist_destructor(&session->s_req_list);
smb_llist_destructor(&session->s_tree_list);
smb_llist_destructor(&session->s_user_list);
@@ -796,41 +889,6 @@ smb_session_cancel_requests(
smb_slist_exit(&session->s_req_list);
}
-void
-smb_session_worker(void *arg)
-{
- smb_request_t *sr;
- smb_srqueue_t *srq;
-
- sr = (smb_request_t *)arg;
- SMB_REQ_VALID(sr);
-
- srq = sr->session->s_srqueue;
- smb_srqueue_waitq_to_runq(srq);
- sr->sr_worker = curthread;
- mutex_enter(&sr->sr_mutex);
- sr->sr_time_active = gethrtime();
- switch (sr->sr_state) {
- case SMB_REQ_STATE_SUBMITTED:
- mutex_exit(&sr->sr_mutex);
- if (smb_dispatch_request(sr)) {
- mutex_enter(&sr->sr_mutex);
- sr->sr_state = SMB_REQ_STATE_COMPLETED;
- mutex_exit(&sr->sr_mutex);
- smb_request_free(sr);
- }
- break;
-
- default:
- ASSERT(sr->sr_state == SMB_REQ_STATE_CANCELED);
- sr->sr_state = SMB_REQ_STATE_COMPLETED;
- mutex_exit(&sr->sr_mutex);
- smb_request_free(sr);
- break;
- }
- smb_srqueue_runq_exit(srq);
-}
-
/*
* Find a user on the specified session by SMB UID.
*/
@@ -1008,7 +1066,7 @@ smb_session_lookup_volume(
void
smb_session_close_pid(
smb_session_t *session,
- uint16_t pid)
+ uint32_t pid)
{
smb_tree_t *tree;
@@ -1270,6 +1328,7 @@ smb_request_alloc(smb_session_t *session, int req_length)
smb_request_t *sr;
ASSERT(session->s_magic == SMB_SESSION_MAGIC);
+ ASSERT(req_length <= session->cmd_max_bytes);
sr = kmem_cache_alloc(smb_cache_request, KM_SLEEP);
@@ -1288,7 +1347,7 @@ smb_request_alloc(smb_session_t *session, int req_length)
sr->sr_gmtoff = session->s_server->si_gmtoff;
sr->sr_cfg = &session->s_cfg;
sr->command.max_bytes = req_length;
- sr->reply.max_bytes = smb_maxbufsize;
+ sr->reply.max_bytes = session->reply_max_bytes;
sr->sr_req_length = req_length;
if (req_length)
sr->sr_request_buf = kmem_alloc(req_length, KM_SLEEP);
@@ -1357,51 +1416,44 @@ boolean_t
smb_session_levelII_oplocks(smb_session_t *session)
{
SMB_SESSION_VALID(session);
- return (session->capabilities & CAP_LEVEL_II_OPLOCKS);
+
+ /* Clients using SMB2 and later always know about oplocks. */
+ if (session->dialect > NT_LM_0_12)
+ return (B_TRUE);
+
+ /* Older clients only do Level II oplocks if negotiated. */
+ if ((session->capabilities & CAP_LEVEL_II_OPLOCKS) != 0)
+ return (B_TRUE);
+
+ return (B_FALSE);
}
/*
* smb_session_oplock_break
*
- * The session lock must NOT be held by the caller of this thread;
- * as this would cause a deadlock.
+ * Send an oplock break request to the client,
+ * recalling some cache delegation.
*/
void
-smb_session_oplock_break(smb_session_t *session,
- uint16_t tid, uint16_t fid, uint8_t brk)
+smb_session_oplock_break(smb_request_t *sr, uint8_t brk)
{
- mbuf_chain_t *mbc;
+ smb_session_t *session = sr->session;
+ mbuf_chain_t *mbc = &sr->reply;
SMB_SESSION_VALID(session);
- mbc = smb_mbc_alloc(MLEN);
-
- (void) smb_mbc_encodef(mbc, "Mb19.wwwwbb3.wbb10.",
- SMB_COM_LOCKING_ANDX,
- tid,
- 0xFFFF, 0, 0xFFFF, 8, 0xFF,
- fid,
- LOCKING_ANDX_OPLOCK_RELEASE,
- (brk == SMB_OPLOCK_BREAK_TO_LEVEL_II) ? 1 : 0);
-
- smb_rwx_rwenter(&session->s_lock, RW_WRITER);
- switch (session->s_state) {
- case SMB_SESSION_STATE_NEGOTIATED:
- case SMB_SESSION_STATE_OPLOCK_BREAKING:
- session->s_state = SMB_SESSION_STATE_OPLOCK_BREAKING;
- (void) smb_session_send(session, 0, mbc);
- smb_mbc_free(mbc);
- break;
-
- case SMB_SESSION_STATE_DISCONNECTED:
- case SMB_SESSION_STATE_TERMINATED:
- smb_mbc_free(mbc);
- break;
-
- default:
- SMB_PANIC();
+ /*
+ * 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);
}
- smb_rwx_rwexit(&session->s_lock);
+
+ (void) smb_session_send(session, 0, mbc);
}
static void
diff --git a/usr/src/uts/common/fs/smbsrv/smb_set_fileinfo.c b/usr/src/uts/common/fs/smbsrv/smb_set_fileinfo.c
index e3d4bdf79e..8f24e1e053 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_set_fileinfo.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_set_fileinfo.c
@@ -66,27 +66,17 @@
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
-typedef struct smb_setinfo {
- uint16_t si_infolev;
- smb_xa_t *si_xa;
- smb_node_t *si_node;
-} smb_setinfo_t;
+static int smb_set_by_fid(smb_request_t *, smb_xa_t *, uint16_t);
+static int smb_set_by_path(smb_request_t *, smb_xa_t *, uint16_t);
/*
- * These functions all return 0 (success) or -1 (error).
- * They set error details in the sr when appropriate.
+ * These functions all return and NT status code.
*/
-static int smb_set_by_fid(smb_request_t *, smb_xa_t *, uint16_t);
-static int smb_set_by_path(smb_request_t *, smb_xa_t *, uint16_t);
-static int smb_set_fileinfo(smb_request_t *, smb_setinfo_t *);
-static int smb_set_information(smb_request_t *, smb_setinfo_t *);
-static int smb_set_information2(smb_request_t *, smb_setinfo_t *);
-static int smb_set_standard_info(smb_request_t *, smb_setinfo_t *);
-static int smb_set_basic_info(smb_request_t *, smb_setinfo_t *);
-static int smb_set_disposition_info(smb_request_t *, smb_setinfo_t *);
-static int smb_set_eof_info(smb_request_t *sr, smb_setinfo_t *);
-static int smb_set_alloc_info(smb_request_t *sr, smb_setinfo_t *);
-static int smb_set_rename_info(smb_request_t *sr, smb_setinfo_t *);
+static uint32_t smb_set_fileinfo(smb_request_t *, smb_setinfo_t *, int);
+static uint32_t smb_set_information(smb_request_t *, smb_setinfo_t *);
+static uint32_t smb_set_information2(smb_request_t *, smb_setinfo_t *);
+static uint32_t smb_set_standard_info(smb_request_t *, smb_setinfo_t *);
+static uint32_t smb_set_rename_info(smb_request_t *sr, smb_setinfo_t *);
/*
* smb_com_trans2_set_file_information
@@ -217,8 +207,9 @@ smb_com_set_information2(smb_request_t *sr)
static int
smb_set_by_fid(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
{
- int rc;
smb_setinfo_t sinfo;
+ uint32_t status;
+ int rc = 0;
if (SMB_TREE_IS_READONLY(sr)) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
@@ -242,10 +233,15 @@ smb_set_by_fid(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
- sinfo.si_xa = xa;
- sinfo.si_infolev = infolev;
+ bzero(&sinfo, sizeof (sinfo));
sinfo.si_node = sr->fid_ofile->f_node;
- rc = smb_set_fileinfo(sr, &sinfo);
+ if (xa != NULL)
+ sinfo.si_data = xa->req_data_mb;
+ status = smb_set_fileinfo(sr, &sinfo, infolev);
+ if (status != 0) {
+ smbsr_error(sr, status, 0, 0);
+ rc = -1;
+ }
smbsr_release_file(sr);
return (rc);
@@ -269,6 +265,7 @@ static int
smb_set_by_path(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
{
int rc;
+ uint32_t status;
smb_setinfo_t sinfo;
smb_node_t *node, *dnode;
char *name;
@@ -305,10 +302,15 @@ smb_set_by_path(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
return (-1);
}
- sinfo.si_xa = xa;
- sinfo.si_infolev = infolev;
+ bzero(&sinfo, sizeof (sinfo));
sinfo.si_node = node;
- rc = smb_set_fileinfo(sr, &sinfo);
+ if (xa != NULL)
+ sinfo.si_data = xa->req_data_mb;
+ status = smb_set_fileinfo(sr, &sinfo, infolev);
+ if (status != 0) {
+ smbsr_error(sr, status, 0, 0);
+ rc = -1;
+ }
smb_node_release(node);
return (rc);
@@ -320,53 +322,63 @@ smb_set_by_path(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
* For compatibility with windows servers, SMB_FILE_LINK_INFORMATION
* is handled by returning NT_STATUS_NOT_SUPPORTED.
*/
-static int
-smb_set_fileinfo(smb_request_t *sr, smb_setinfo_t *sinfo)
+static uint32_t
+smb_set_fileinfo(smb_request_t *sr, smb_setinfo_t *sinfo, int infolev)
{
- switch (sinfo->si_infolev) {
+ uint32_t status;
+
+ switch (infolev) {
case SMB_SET_INFORMATION:
- return (smb_set_information(sr, sinfo));
+ status = smb_set_information(sr, sinfo);
+ break;
case SMB_SET_INFORMATION2:
- return (smb_set_information2(sr, sinfo));
+ status = smb_set_information2(sr, sinfo);
+ break;
case SMB_INFO_STANDARD:
- return (smb_set_standard_info(sr, sinfo));
+ status = smb_set_standard_info(sr, sinfo);
+ break;
case SMB_INFO_SET_EAS:
/* EAs not supported */
- return (0);
+ status = 0;
+ break;
case SMB_SET_FILE_BASIC_INFO:
case SMB_FILE_BASIC_INFORMATION:
- return (smb_set_basic_info(sr, sinfo));
+ status = smb_set_basic_info(sr, sinfo);
+ break;
case SMB_SET_FILE_DISPOSITION_INFO:
case SMB_FILE_DISPOSITION_INFORMATION:
- return (smb_set_disposition_info(sr, sinfo));
+ status = smb_set_disposition_info(sr, sinfo);
+ break;
case SMB_SET_FILE_END_OF_FILE_INFO:
case SMB_FILE_END_OF_FILE_INFORMATION:
- return (smb_set_eof_info(sr, sinfo));
+ status = smb_set_eof_info(sr, sinfo);
+ break;
case SMB_SET_FILE_ALLOCATION_INFO:
case SMB_FILE_ALLOCATION_INFORMATION:
- return (smb_set_alloc_info(sr, sinfo));
+ status = smb_set_alloc_info(sr, sinfo);
+ break;
case SMB_FILE_RENAME_INFORMATION:
- return (smb_set_rename_info(sr, sinfo));
+ status = smb_set_rename_info(sr, sinfo);
+ break;
case SMB_FILE_LINK_INFORMATION:
- smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
- ERRDOS, ERROR_NOT_SUPPORTED);
- return (-1);
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+
default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
break;
}
- smbsr_error(sr, NT_STATUS_INVALID_INFO_CLASS,
- ERRDOS, ERROR_INVALID_PARAMETER);
- return (-1);
+ return (status);
}
/*
@@ -379,23 +391,22 @@ smb_set_fileinfo(smb_request_t *sr, smb_setinfo_t *sinfo)
* attributes have ONLY FILE_ATTRIBUTE_NORMAL set do NOT change
* the file's attributes.
*/
-static int
+static uint32_t
smb_set_information(smb_request_t *sr, smb_setinfo_t *sinfo)
{
- int rc;
- uint16_t attributes;
- smb_node_t *node = sinfo->si_node;
smb_attr_t attr;
+ smb_node_t *node = sinfo->si_node;
+ uint32_t status = 0;
uint32_t mtime;
+ uint16_t attributes;
+ int rc;
if (smbsr_decode_vwv(sr, "wl10.", &attributes, &mtime) != 0)
- return (-1);
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) &&
(!smb_node_is_dir(node))) {
- smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
- ERRDOS, ERROR_INVALID_PARAMETER);
- return (-1);
+ return (NT_STATUS_INVALID_PARAMETER);
}
bzero(&attr, sizeof (smb_attr_t));
@@ -411,26 +422,25 @@ smb_set_information(smb_request_t *sr, smb_setinfo_t *sinfo)
}
rc = smb_node_setattr(sr, node, sr->user_cr, NULL, &attr);
- if (rc != 0) {
- smbsr_errno(sr, rc);
- return (-1);
- }
+ if (rc != 0)
+ status = smb_errno2status(rc);
- return (0);
+ return (status);
}
/*
* smb_set_information2
*/
-static int
+static uint32_t
smb_set_information2(smb_request_t *sr, smb_setinfo_t *sinfo)
{
- int rc;
- uint32_t crtime, atime, mtime;
smb_attr_t attr;
+ uint32_t crtime, atime, mtime;
+ uint32_t status = 0;
+ int rc;
if (smbsr_decode_vwv(sr, "yyy", &crtime, &atime, &mtime) != 0)
- return (-1);
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
bzero(&attr, sizeof (smb_attr_t));
if (mtime != 0 && mtime != UINT_MAX) {
@@ -452,12 +462,10 @@ smb_set_information2(smb_request_t *sr, smb_setinfo_t *sinfo)
rc = smb_node_setattr(sr, sinfo->si_node, sr->user_cr,
sr->fid_ofile, &attr);
- if (rc != 0) {
- smbsr_errno(sr, rc);
- return (-1);
- }
+ if (rc != 0)
+ status = smb_errno2status(rc);
- return (0);
+ return (status);
}
/*
@@ -465,18 +473,18 @@ smb_set_information2(smb_request_t *sr, smb_setinfo_t *sinfo)
*
* Sets standard file/path information.
*/
-static int
+static uint32_t
smb_set_standard_info(smb_request_t *sr, smb_setinfo_t *sinfo)
{
smb_attr_t attr;
- uint32_t crtime, atime, mtime;
smb_node_t *node = sinfo->si_node;
+ uint32_t crtime, atime, mtime;
+ uint32_t status = 0;
int rc;
- if (smb_mbc_decodef(&sinfo->si_xa->req_data_mb, "yyy",
- &crtime, &atime, &mtime) != 0) {
- return (-1);
- }
+ if (smb_mbc_decodef(&sinfo->si_data, "yyy",
+ &crtime, &atime, &mtime) != 0)
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
bzero(&attr, sizeof (smb_attr_t));
if (mtime != 0 && mtime != (uint32_t)-1) {
@@ -497,240 +505,18 @@ smb_set_standard_info(smb_request_t *sr, smb_setinfo_t *sinfo)
}
rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr);
- if (rc != 0) {
- smbsr_errno(sr, rc);
- return (-1);
- }
-
- return (0);
-}
-
-/*
- * smb_set_basic_info
- *
- * Sets basic file/path information.
- *
- * It is not valid to set FILE_ATTRIBUTE_DIRECTORY if the
- * target is not a directory.
- *
- * For compatibility with windows servers:
- * - if the specified attributes have ONLY FILE_ATTRIBUTE_NORMAL set
- * clear (0) the file's attributes.
- * - if the specified attributes are 0 do NOT change the file's attributes.
- */
-static int
-smb_set_basic_info(smb_request_t *sr, smb_setinfo_t *sinfo)
-{
- int rc;
- uint64_t crtime, atime, mtime, ctime;
- uint16_t attributes;
- smb_attr_t attr;
- smb_node_t *node = sinfo->si_node;
-
- if (smb_mbc_decodef(&sinfo->si_xa->req_data_mb, "qqqqw",
- &crtime, &atime, &mtime, &ctime, &attributes) != 0) {
- return (-1);
- }
-
- if ((attributes & FILE_ATTRIBUTE_DIRECTORY) &&
- (!smb_node_is_dir(node))) {
- smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
- ERRDOS, ERROR_INVALID_PARAMETER);
- return (-1);
- }
-
- bzero(&attr, sizeof (smb_attr_t));
- if (ctime != 0 && ctime != (uint64_t)-1) {
- smb_time_nt_to_unix(ctime, &attr.sa_vattr.va_ctime);
- attr.sa_mask |= SMB_AT_CTIME;
- }
-
- if (crtime != 0 && crtime != (uint64_t)-1) {
- smb_time_nt_to_unix(crtime, &attr.sa_crtime);
- attr.sa_mask |= SMB_AT_CRTIME;
- }
-
- if (mtime != 0 && mtime != (uint64_t)-1) {
- smb_time_nt_to_unix(mtime, &attr.sa_vattr.va_mtime);
- attr.sa_mask |= SMB_AT_MTIME;
- }
-
- if (atime != 0 && atime != (uint64_t)-1) {
- smb_time_nt_to_unix(atime, &attr.sa_vattr.va_atime);
- attr.sa_mask |= SMB_AT_ATIME;
- }
-
- if (attributes != 0) {
- attr.sa_dosattr = attributes;
- attr.sa_mask |= SMB_AT_DOSATTR;
- }
-
- rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr);
- if (rc != 0) {
- smbsr_errno(sr, rc);
- return (-1);
- }
-
- return (0);
-}
-
-/*
- * smb_set_eof_info
- */
-static int
-smb_set_eof_info(smb_request_t *sr, smb_setinfo_t *sinfo)
-{
- int rc;
- smb_attr_t attr;
- uint64_t eof;
- smb_node_t *node = sinfo->si_node;
-
- if (smb_mbc_decodef(&sinfo->si_xa->req_data_mb, "q", &eof) != 0)
- return (-1);
-
- if (smb_node_is_dir(node)) {
- smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
- ERRDOS, ERROR_INVALID_PARAMETER);
- return (-1);
- }
-
- /* If opened by path, break exclusive oplock */
- if (sr->fid_ofile == NULL)
- (void) smb_oplock_break(sr, node,
- SMB_OPLOCK_BREAK_EXCLUSIVE | SMB_OPLOCK_BREAK_TO_NONE);
-
- bzero(&attr, sizeof (smb_attr_t));
- attr.sa_mask = SMB_AT_SIZE;
- attr.sa_vattr.va_size = (u_offset_t)eof;
- rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr);
- if (rc != 0) {
- smbsr_errno(sr, rc);
- return (-1);
- }
-
- smb_oplock_break_levelII(node);
- return (0);
-}
-
-/*
- * smb_set_alloc_info
- */
-static int
-smb_set_alloc_info(smb_request_t *sr, smb_setinfo_t *sinfo)
-{
- int rc;
- smb_attr_t attr;
- uint64_t allocsz;
- smb_node_t *node = sinfo->si_node;
-
- if (smb_mbc_decodef(&sinfo->si_xa->req_data_mb, "q", &allocsz) != 0)
- return (-1);
-
- if (smb_node_is_dir(node)) {
- smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
- ERRDOS, ERROR_INVALID_PARAMETER);
- return (-1);
- }
-
- /* If opened by path, break exclusive oplock */
- if (sr->fid_ofile == NULL)
- (void) smb_oplock_break(sr, node,
- SMB_OPLOCK_BREAK_EXCLUSIVE | SMB_OPLOCK_BREAK_TO_NONE);
-
- bzero(&attr, sizeof (smb_attr_t));
- attr.sa_mask = SMB_AT_ALLOCSZ;
- attr.sa_allocsz = (u_offset_t)allocsz;
- rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr);
- if (rc != 0) {
- smbsr_errno(sr, rc);
- return (-1);
- }
-
- smb_oplock_break_levelII(node);
- return (0);
-}
-
-/*
- * smb_set_disposition_info
- *
- * Set/Clear DELETE_ON_CLOSE flag for an open file.
- * File should have been opened with DELETE access otherwise
- * the operation is not permitted.
- *
- * NOTE: The node should be marked delete-on-close upon the receipt
- * of the Trans2SetFileInfo(SetDispositionInfo) if mark_delete is set.
- * It is different than both SmbNtCreateAndX and SmbNtTransact, which
- * set delete-on-close on the ofile and defer setting the flag on the
- * node until the file is closed.
- *
- * Observation of Windows 2000 indicates the following:
- *
- * 1) If a file is not opened with delete-on-close create options and
- * the delete-on-close is set via Trans2SetFileInfo(SetDispositionInfo)
- * using that open file handle, any subsequent open requests will fail
- * with DELETE_PENDING.
- *
- * 2) If a file is opened with delete-on-close create options and the
- * client attempts to unset delete-on-close via Trans2SetFileInfo
- * (SetDispositionInfo) prior to the file close, any subsequent open
- * requests will still fail with DELETE_PENDING after the file is closed.
- *
- * 3) If a file is opened with delete-on-close create options and that
- * file handle (not the last open handle and the only file handle
- * with delete-on-close set) is closed. Any subsequent open requests
- * will fail with DELETE_PENDING. Unsetting delete-on-close via
- * Trans2SetFileInfo(SetDispositionInfo) at this time will unset the
- * node delete-on-close flag, which will result in the file not being
- * removed even after the last file handle is closed.
- */
-static int
-smb_set_disposition_info(smb_request_t *sr, smb_setinfo_t *sinfo)
-{
- unsigned char mark_delete;
- uint32_t flags = 0;
- int doserr;
- uint32_t status;
-
- if (smb_mbc_decodef(&sinfo->si_xa->req_data_mb, "b", &mark_delete) != 0)
- return (-1);
-
- if ((sr->fid_ofile == NULL) ||
- !(smb_ofile_granted_access(sr->fid_ofile) & DELETE)) {
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
- ERRDOS, ERROR_ACCESS_DENIED);
- return (-1);
- }
+ if (rc != 0)
+ status = smb_errno2status(rc);
- if (mark_delete) {
- if (SMB_TREE_SUPPORTS_CATIA(sr))
- flags |= SMB_CATIA;
-
- status = smb_node_set_delete_on_close(sinfo->si_node,
- sr->user_cr, flags);
- if (status != NT_STATUS_SUCCESS) {
- switch (status) {
- case NT_STATUS_CANNOT_DELETE:
- doserr = ERROR_ACCESS_DENIED;
- break;
- case NT_STATUS_DIRECTORY_NOT_EMPTY:
- doserr = ERROR_DIR_NOT_EMPTY;
- break;
- default:
- doserr = ERROR_GEN_FAILURE;
- break;
- }
- smbsr_error(sr, status, ERRDOS, doserr);
- return (-1);
- }
- } else {
- smb_node_reset_delete_on_close(sinfo->si_node);
- }
- return (0);
+ return (status);
}
/*
* smb_set_rename_info
*
+ * This call only allows a rename in the same directory, and the
+ * directory name is not part of the new name provided.
+ *
* Explicitly specified parameter validation rules:
* - If rootdir is not NULL respond with NT_STATUS_INVALID_PARAMETER.
* - If the filename contains a separator character respond with
@@ -740,35 +526,64 @@ smb_set_disposition_info(smb_request_t *sr, smb_setinfo_t *sinfo)
* Some Windows servers break BATCH oplocks prior to the rename.
* W2K3 does not. We behave as W2K3; we do not send an oplock break.
*/
-static int
+static uint32_t
smb_set_rename_info(smb_request_t *sr, smb_setinfo_t *sinfo)
{
- int rc;
- uint32_t flags, rootdir, namelen;
+ smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
+ smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
char *fname;
+ char *path;
+ uint8_t flags;
+ uint32_t rootdir, namelen;
+ uint32_t status = 0;
+ int rc;
- rc = smb_mbc_decodef(&sinfo->si_xa->req_data_mb, "lll",
+ rc = smb_mbc_decodef(&sinfo->si_data, "b...ll",
&flags, &rootdir, &namelen);
if (rc == 0) {
- rc = smb_mbc_decodef(&sinfo->si_xa->req_data_mb, "%#U",
+ rc = smb_mbc_decodef(&sinfo->si_data, "%#U",
sr, namelen, &fname);
}
if (rc != 0)
- return (-1);
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
if ((rootdir != 0) || (namelen == 0) || (namelen >= MAXNAMELEN)) {
- smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
- ERRDOS, ERROR_INVALID_PARAMETER);
- return (-1);
+ return (NT_STATUS_INVALID_PARAMETER);
}
if (strchr(fname, '\\') != NULL) {
- smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
- ERRDOS, ERROR_NOT_SUPPORTED);
- return (-1);
+ return (NT_STATUS_NOT_SUPPORTED);
+ }
+
+ /*
+ * Construct the full dst. path relative to the share root.
+ * Allocated path is free'd in smb_request_free.
+ */
+ path = smb_srm_zalloc(sr, SMB_MAXPATHLEN);
+ if (src_fqi->fq_path.pn_pname) {
+ /* Got here via: smb_set_by_path */
+ (void) snprintf(path, SMB_MAXPATHLEN, "%s\\%s",
+ src_fqi->fq_path.pn_pname, fname);
+ } else {
+ /* Got here via: smb_set_by_fid */
+ rc = smb_node_getshrpath(sinfo->si_node->n_dnode,
+ sr->tid_tree, path, SMB_MAXPATHLEN);
+ if (rc != 0) {
+ status = smb_errno2status(rc);
+ return (status);
+ }
+ (void) strlcat(path, "\\", SMB_MAXPATHLEN);
+ (void) strlcat(path, fname, SMB_MAXPATHLEN);
}
- rc = smb_trans2_rename(sr, sinfo->si_node, fname, flags);
+ /*
+ * The common rename code can slightly optimize a
+ * rename in the same directory when we set the
+ * dst_fqi->fq_dnode, dst_fqi->fq_last_comp
+ */
+ dst_fqi->fq_dnode = sinfo->si_node->n_dnode;
+ (void) strlcpy(dst_fqi->fq_last_comp, fname, MAXNAMELEN);
- return ((rc == 0) ? 0 : -1);
+ status = smb_setinfo_rename(sr, sinfo->si_node, path, flags);
+ return (status);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_sign_kcf.c b/usr/src/uts/common/fs/smbsrv/smb_sign_kcf.c
index 45b7f31d4d..fac52cab21 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_sign_kcf.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_sign_kcf.c
@@ -14,7 +14,7 @@
*/
/*
- * Helper functions for SMB1 signing using the
+ * Helper functions for SMB signing using the
* Kernel Cryptographic Framework (KCF)
*
* There are two implementations of these functions:
@@ -97,3 +97,84 @@ smb_md5_final(smb_sign_ctx_t ctx, uint8_t *digest16)
return (rv == CRYPTO_SUCCESS ? 0 : -1);
}
+
+/*
+ * SMB2 signing helpers:
+ * (getmech, init, update, final)
+ */
+
+int
+smb2_hmac_getmech(smb_sign_mech_t *mech)
+{
+ crypto_mech_type_t t;
+
+ t = crypto_mech2id(SUN_CKM_SHA256_HMAC);
+ if (t == CRYPTO_MECH_INVALID)
+ return (-1);
+ mech->cm_type = t;
+ return (0);
+}
+
+/*
+ * Start the KCF session, load the key
+ */
+int
+smb2_hmac_init(smb_sign_ctx_t *ctxp, smb_sign_mech_t *mech,
+ uint8_t *key, size_t key_len)
+{
+ crypto_key_t ckey;
+ int rv;
+
+ bzero(&ckey, sizeof (ckey));
+ ckey.ck_format = CRYPTO_KEY_RAW;
+ ckey.ck_data = key;
+ ckey.ck_length = key_len * 8; /* in bits */
+
+ rv = crypto_mac_init(mech, &ckey, NULL, ctxp, NULL);
+
+ return (rv == CRYPTO_SUCCESS ? 0 : -1);
+}
+
+/*
+ * Digest one segment
+ */
+int
+smb2_hmac_update(smb_sign_ctx_t ctx, uint8_t *in, size_t len)
+{
+ crypto_data_t data;
+ int rv;
+
+ bzero(&data, sizeof (data));
+ data.cd_format = CRYPTO_DATA_RAW;
+ data.cd_length = len;
+ data.cd_raw.iov_base = (void *)in;
+ data.cd_raw.iov_len = len;
+
+ rv = crypto_mac_update(ctx, &data, 0);
+
+ return (rv == CRYPTO_SUCCESS ? 0 : -1);
+}
+
+/*
+ * Note, the SMB2 signature is the first 16 bytes of the
+ * 32-byte SHA256 HMAC digest.
+ */
+int
+smb2_hmac_final(smb_sign_ctx_t ctx, uint8_t *digest16)
+{
+ uint8_t full_digest[SHA256_DIGEST_LENGTH];
+ crypto_data_t out;
+ int rv;
+
+ bzero(&out, sizeof (out));
+ out.cd_format = CRYPTO_DATA_RAW;
+ out.cd_length = SHA256_DIGEST_LENGTH;
+ out.cd_raw.iov_len = SHA256_DIGEST_LENGTH;
+ out.cd_raw.iov_base = (void *)full_digest;
+
+ rv = crypto_mac_final(ctx, &out, 0);
+ if (rv == CRYPTO_SUCCESS)
+ bcopy(full_digest, digest16, 16);
+
+ return (rv == CRYPTO_SUCCESS ? 0 : -1);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_dfs.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_dfs.c
index 337490a836..df874ffe1d 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_trans2_dfs.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_dfs.c
@@ -26,59 +26,7 @@
*/
#include <smbsrv/smb_kproto.h>
-#include <smbsrv/smb_dfs.h>
-#include <smbsrv/smb_door.h>
-
-/*
- * Get Referral response header flags
- * For exact meaning refer to MS-DFSC spec.
- *
- * R: ReferralServers
- * S: StorageServers
- * T: TargetFailback
- */
-#define DFS_HDRFLG_R 0x00000001
-#define DFS_HDRFLG_S 0x00000002
-#define DFS_HDRFLG_T 0x00000004
-
-/*
- * Entry flags
- */
-#define DFS_ENTFLG_T 0x0004
-
-/*
- * Referral entry types/versions
- */
-#define DFS_REFERRAL_V1 0x0001
-#define DFS_REFERRAL_V2 0x0002
-#define DFS_REFERRAL_V3 0x0003
-#define DFS_REFERRAL_V4 0x0004
-
-/*
- * Valid values for ServerType field in referral entries
- */
-#define DFS_SRVTYPE_NONROOT 0x0000
-#define DFS_SRVTYPE_ROOT 0x0001
-
-/*
- * Size of the fix part for each referral entry type
- */
-#define DFS_REFV1_ENTSZ 8
-#define DFS_REFV2_ENTSZ 22
-#define DFS_REFV3_ENTSZ 34
-#define DFS_REFV4_ENTSZ 34
-
-static dfs_reftype_t smb_dfs_get_reftype(const char *);
-static void smb_dfs_encode_hdr(smb_xa_t *, dfs_info_t *);
-static uint32_t smb_dfs_encode_refv1(smb_request_t *, smb_xa_t *, dfs_info_t *);
-static uint32_t smb_dfs_encode_refv2(smb_request_t *, smb_xa_t *, dfs_info_t *);
-static uint32_t smb_dfs_encode_refv3_v4(smb_request_t *, smb_xa_t *,
- dfs_info_t *, uint16_t);
-static void smb_dfs_encode_targets(smb_xa_t *, dfs_info_t *);
-static uint32_t smb_dfs_referrals_get(smb_request_t *, char *, dfs_reftype_t,
- dfs_referral_response_t *);
-static void smb_dfs_referrals_free(dfs_referral_response_t *);
-static uint16_t smb_dfs_referrals_unclen(dfs_info_t *, uint16_t);
+#include <smbsrv/winioctl.h>
/*
* [MS-CIFS]
@@ -104,12 +52,9 @@ smb_com_trans2_report_dfs_inconsistency(smb_request_t *sr)
smb_sdrc_t
smb_com_trans2_get_dfs_referral(smb_request_t *sr, smb_xa_t *xa)
{
- dfs_info_t *referrals;
- dfs_referral_response_t refrsp;
- dfs_reftype_t reftype;
- uint16_t maxreflvl;
+ smb_fsctl_t fsctl;
uint32_t status;
- char *path;
+ uint16_t doserr;
/* This request is only valid over IPC connections */
if (!STYPE_ISIPC(sr->tid_tree->t_res_type)) {
@@ -118,422 +63,25 @@ smb_com_trans2_get_dfs_referral(smb_request_t *sr, smb_xa_t *xa)
return (SDRC_ERROR);
}
- if (smb_mbc_decodef(&xa->req_param_mb, "%wu", sr, &maxreflvl, &path)
- != 0) {
- return (SDRC_ERROR);
- }
+ fsctl.CtlCode = FSCTL_DFS_GET_REFERRALS;
+ fsctl.InputCount = xa->smb_tpscnt;
+ fsctl.OutputCount = 0;
+ fsctl.MaxOutputResp = xa->smb_mdrcnt;
+ fsctl.in_mbc = &xa->req_param_mb;
+ fsctl.out_mbc = &xa->rep_data_mb;
- reftype = smb_dfs_get_reftype((const char *)path);
+ status = smb_dfs_get_referrals(sr, &fsctl);
- switch (reftype) {
- case DFS_REFERRAL_INVALID:
- /* Need to check the error for this case */
- smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS,
- ERROR_INVALID_PARAMETER);
- return (SDRC_ERROR);
+ /* Out param is the API-level return code. */
+ doserr = smb_status2doserr(status);
+ (void) smb_mbc_encodef(&xa->rep_param_mb, "w", doserr);
- case DFS_REFERRAL_DOMAIN:
- case DFS_REFERRAL_DC:
- /* MS-DFSC: this error is returned by non-DC root */
- smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS,
- ERROR_INVALID_PARAMETER);
+#if 0 /* XXX - Is API-level return code enough? */
+ if (status) {
+ smbsr_error(sr, NT_STATUS_NO_SUCH_DEVICE, 0, 0);
return (SDRC_ERROR);
-
- case DFS_REFERRAL_SYSVOL:
- /* MS-DFSC: this error is returned by non-DC root */
- smbsr_error(sr, NT_STATUS_NO_SUCH_DEVICE, ERRDOS,
- ERROR_BAD_DEVICE);
- return (SDRC_ERROR);
-
- default:
- break;
- }
-
- status = smb_dfs_referrals_get(sr, path, reftype, &refrsp);
- if (status != NT_STATUS_SUCCESS)
- return (SDRC_ERROR);
-
- referrals = &refrsp.rp_referrals;
- smb_dfs_encode_hdr(xa, referrals);
-
- /*
- * Server can respond with a referral version which is not
- * bigger than but could be less than the maximum specified
- * in the request.
- */
- switch (maxreflvl) {
- case DFS_REFERRAL_V1:
- status = smb_dfs_encode_refv1(sr, xa, referrals);
- break;
-
- case DFS_REFERRAL_V2:
- status = smb_dfs_encode_refv2(sr, xa, referrals);
- break;
-
- case DFS_REFERRAL_V3:
- status = smb_dfs_encode_refv3_v4(sr, xa, referrals, maxreflvl);
- break;
-
- default:
- status = smb_dfs_encode_refv3_v4(sr, xa, referrals,
- DFS_REFERRAL_V4);
- break;
- }
-
- smb_dfs_referrals_free(&refrsp);
-
- return ((status == NT_STATUS_SUCCESS) ? SDRC_SUCCESS : SDRC_ERROR);
-}
-
-/*
- * [MS-DFSC]: REQ_GET_DFS_REFERRAL
- *
- * Determines the referral type based on the specified path:
- *
- * Domain referral:
- * ""
- *
- * DC referral:
- * \<domain>
- *
- * Sysvol referral:
- * \<domain>\SYSVOL
- * \<domain>\NETLOGON
- *
- * Root referral:
- * \<domain>\<dfsname>
- * \<server>\<dfsname>
- *
- * Link referral:
- * \<domain>\<dfsname>\<linkpath>
- * \<server>\<dfsname>\<linkpath>
- */
-static dfs_reftype_t
-smb_dfs_get_reftype(const char *path)
-{
- smb_unc_t unc;
- dfs_reftype_t reftype = 0;
-
- if (*path == '\0')
- return (DFS_REFERRAL_DOMAIN);
-
- if (smb_unc_init(path, &unc) != 0)
- return (DFS_REFERRAL_INVALID);
-
- if (unc.unc_path != NULL) {
- reftype = DFS_REFERRAL_LINK;
- } else if (unc.unc_share != NULL) {
- if ((smb_strcasecmp(unc.unc_share, "SYSVOL", 0) == 0) ||
- (smb_strcasecmp(unc.unc_share, "NETLOGON", 0) == 0)) {
- reftype = DFS_REFERRAL_SYSVOL;
- } else {
- reftype = DFS_REFERRAL_ROOT;
- }
- } else if (unc.unc_server != NULL) {
- reftype = DFS_REFERRAL_DC;
- }
-
- smb_unc_free(&unc);
- return (reftype);
-}
-
-static void
-smb_dfs_encode_hdr(smb_xa_t *xa, dfs_info_t *referrals)
-{
- uint16_t path_consumed;
- uint32_t flags;
-
- path_consumed = smb_wcequiv_strlen(referrals->i_uncpath);
- flags = DFS_HDRFLG_S;
- if (referrals->i_type == DFS_OBJECT_ROOT)
- flags |= DFS_HDRFLG_R;
-
- (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
- (void) smb_mbc_encodef(&xa->rep_data_mb, "wwl", path_consumed,
- referrals->i_ntargets, flags);
-}
-
-static uint32_t
-smb_dfs_encode_refv1(smb_request_t *sr, smb_xa_t *xa, dfs_info_t *referrals)
-{
- uint16_t entsize, rep_bufsize;
- uint16_t server_type;
- uint16_t flags = 0;
- uint16_t r;
- char *target;
-
- rep_bufsize = MBC_MAXBYTES(&xa->rep_data_mb);
-
- server_type = (referrals->i_type == DFS_OBJECT_ROOT) ?
- DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT;
-
- target = kmem_alloc(MAXPATHLEN, KM_SLEEP);
-
- for (r = 0; r < referrals->i_ntargets; r++) {
- (void) snprintf(target, MAXPATHLEN, "\\%s\\%s",
- referrals->i_targets[r].t_server,
- referrals->i_targets[r].t_share);
-
- entsize = DFS_REFV1_ENTSZ + smb_wcequiv_strlen(target) + 2;
- if (entsize > rep_bufsize)
- break;
-
- (void) smb_mbc_encodef(&xa->rep_data_mb, "wwwwU",
- DFS_REFERRAL_V1, entsize, server_type, flags, target);
- rep_bufsize -= entsize;
- }
-
- kmem_free(target, MAXPATHLEN);
-
- /*
- * Need room for at least one entry.
- * Windows will silently drop targets that do not fit in
- * the response buffer.
- */
- if (r == 0) {
- smbsr_warn(sr, NT_STATUS_BUFFER_OVERFLOW,
- ERRDOS, ERROR_MORE_DATA);
- return (NT_STATUS_BUFFER_OVERFLOW);
- }
-
- return (NT_STATUS_SUCCESS);
-}
-
-/*
- * Prepare a response with V2 referral format.
- *
- * Here is the response packet format.
- * All the strings come after all the fixed size entry headers.
- * These headers contain offsets to the strings at the end. Note
- * that the two "dfs_path" after the last entry is shared between
- * all the entries.
- *
- * ent1-hdr
- * ent2-hdr
- * ...
- * entN-hdr
- * dfs_path
- * dfs_path
- * target1
- * target2
- * ...
- * targetN
- *
- * MS-DFSC mentions that strings can come after each entry header or all after
- * the last entry header. Windows responses are in the format above.
- */
-static uint32_t
-smb_dfs_encode_refv2(smb_request_t *sr, smb_xa_t *xa, dfs_info_t *referrals)
-{
- uint16_t entsize, rep_bufsize;
- uint16_t server_type;
- uint16_t flags = 0;
- uint32_t proximity = 0;
- uint16_t path_offs, altpath_offs, netpath_offs;
- uint16_t targetsz, total_targetsz = 0;
- uint16_t dfs_pathsz;
- uint16_t r;
-
- rep_bufsize = MBC_MAXBYTES(&xa->rep_data_mb);
- dfs_pathsz = smb_wcequiv_strlen(referrals->i_uncpath) + 2;
- entsize = DFS_REFV2_ENTSZ + dfs_pathsz + dfs_pathsz +
- smb_dfs_referrals_unclen(referrals, 0);
-
- if (entsize > rep_bufsize) {
- /* need room for at least one referral */
- smbsr_warn(sr, NT_STATUS_BUFFER_OVERFLOW,
- ERRDOS, ERROR_MORE_DATA);
- return (NT_STATUS_BUFFER_OVERFLOW);
- }
-
- server_type = (referrals->i_type == DFS_OBJECT_ROOT) ?
- DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT;
-
- rep_bufsize -= entsize;
- entsize = DFS_REFV2_ENTSZ;
-
- for (r = 0; r < referrals->i_ntargets; r++) {
- path_offs = (referrals->i_ntargets - r) * DFS_REFV2_ENTSZ;
- altpath_offs = path_offs + dfs_pathsz;
- netpath_offs = altpath_offs + dfs_pathsz + total_targetsz;
- targetsz = smb_dfs_referrals_unclen(referrals, r);
-
- if (r != 0) {
- entsize = DFS_REFV2_ENTSZ + targetsz;
- if (entsize > rep_bufsize)
- /* silently drop targets that do not fit */
- break;
- rep_bufsize -= entsize;
- }
-
- (void) smb_mbc_encodef(&xa->rep_data_mb, "wwwwllwww",
- DFS_REFERRAL_V2, DFS_REFV2_ENTSZ, server_type, flags,
- proximity, referrals->i_timeout, path_offs, altpath_offs,
- netpath_offs);
-
- total_targetsz += targetsz;
- }
-
- smb_dfs_encode_targets(xa, referrals);
-
- return (NT_STATUS_SUCCESS);
-}
-
-/*
- * Prepare a response with V3/V4 referral format.
- *
- * For more details, see comments for smb_dfs_encode_refv2() or see
- * MS-DFSC specification.
- */
-static uint32_t
-smb_dfs_encode_refv3_v4(smb_request_t *sr, smb_xa_t *xa, dfs_info_t *referrals,
- uint16_t ver)
-{
- uint16_t entsize, rep_bufsize, hdrsize;
- uint16_t server_type;
- uint16_t flags = 0;
- uint16_t path_offs, altpath_offs, netpath_offs;
- uint16_t targetsz, total_targetsz = 0;
- uint16_t dfs_pathsz;
- uint16_t r;
-
- hdrsize = (ver == DFS_REFERRAL_V3) ? DFS_REFV3_ENTSZ : DFS_REFV4_ENTSZ;
- rep_bufsize = MBC_MAXBYTES(&xa->rep_data_mb);
- dfs_pathsz = smb_wcequiv_strlen(referrals->i_uncpath) + 2;
- entsize = hdrsize + dfs_pathsz + dfs_pathsz +
- smb_dfs_referrals_unclen(referrals, 0);
-
- if (entsize > rep_bufsize) {
- /* need room for at least one referral */
- smbsr_warn(sr, NT_STATUS_BUFFER_OVERFLOW,
- ERRDOS, ERROR_MORE_DATA);
- return (NT_STATUS_BUFFER_OVERFLOW);
- }
-
- server_type = (referrals->i_type == DFS_OBJECT_ROOT) ?
- DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT;
-
- rep_bufsize -= entsize;
-
- for (r = 0; r < referrals->i_ntargets; r++) {
- path_offs = (referrals->i_ntargets - r) * hdrsize;
- altpath_offs = path_offs + dfs_pathsz;
- netpath_offs = altpath_offs + dfs_pathsz + total_targetsz;
- targetsz = smb_dfs_referrals_unclen(referrals, r);
-
- if (r != 0) {
- entsize = hdrsize + targetsz;
- if (entsize > rep_bufsize)
- /* silently drop targets that do not fit */
- break;
- rep_bufsize -= entsize;
- flags = 0;
- } else if (ver == DFS_REFERRAL_V4) {
- flags = DFS_ENTFLG_T;
- }
-
- (void) smb_mbc_encodef(&xa->rep_data_mb, "wwwwlwww16.",
- ver, hdrsize, server_type, flags,
- referrals->i_timeout, path_offs, altpath_offs,
- netpath_offs);
-
- total_targetsz += targetsz;
}
+#endif
- smb_dfs_encode_targets(xa, referrals);
-
- return (NT_STATUS_SUCCESS);
-}
-
-/*
- * Encodes DFS path, and target strings which come after fixed header
- * entries.
- *
- * Windows 2000 and earlier set the DFSAlternatePathOffset to point to
- * an 8.3 string representation of the string pointed to by
- * DFSPathOffset if it is not a legal 8.3 string. Otherwise, if
- * DFSPathOffset points to a legal 8.3 string, DFSAlternatePathOffset
- * points to a separate copy of the same string. Windows Server 2003,
- * Windows Server 2008 and Windows Server 2008 R2 set the
- * DFSPathOffset and DFSAlternatePathOffset fields to point to separate
- * copies of the identical string.
- *
- * Following Windows 2003 and later here.
- */
-static void
-smb_dfs_encode_targets(smb_xa_t *xa, dfs_info_t *referrals)
-{
- char *target;
- int r;
-
- (void) smb_mbc_encodef(&xa->rep_data_mb, "UU", referrals->i_uncpath,
- referrals->i_uncpath);
-
- target = kmem_alloc(MAXPATHLEN, KM_SLEEP);
- for (r = 0; r < referrals->i_ntargets; r++) {
- (void) snprintf(target, MAXPATHLEN, "\\%s\\%s",
- referrals->i_targets[r].t_server,
- referrals->i_targets[r].t_share);
- (void) smb_mbc_encodef(&xa->rep_data_mb, "U", target);
- }
- kmem_free(target, MAXPATHLEN);
-}
-
-/*
- * Get referral information for the specified path from user space
- * using a door call.
- */
-static uint32_t
-smb_dfs_referrals_get(smb_request_t *sr, char *dfs_path, dfs_reftype_t reftype,
- dfs_referral_response_t *refrsp)
-{
- dfs_referral_query_t req;
- int rc;
-
- req.rq_type = reftype;
- req.rq_path = dfs_path;
-
- bzero(refrsp, sizeof (dfs_referral_response_t));
- refrsp->rp_status = NT_STATUS_NOT_FOUND;
-
- rc = smb_kdoor_upcall(sr->sr_server, SMB_DR_DFS_GET_REFERRALS,
- &req, dfs_referral_query_xdr, refrsp, dfs_referral_response_xdr);
-
- if (rc != 0 || refrsp->rp_status != ERROR_SUCCESS) {
- smbsr_error(sr, NT_STATUS_NO_SUCH_DEVICE, ERRDOS,
- ERROR_BAD_DEVICE);
- return (NT_STATUS_NO_SUCH_DEVICE);
- }
-
- (void) strsubst(refrsp->rp_referrals.i_uncpath, '/', '\\');
- return (NT_STATUS_SUCCESS);
-}
-
-static void
-smb_dfs_referrals_free(dfs_referral_response_t *refrsp)
-{
- xdr_free(dfs_referral_response_xdr, (char *)refrsp);
-}
-
-/*
- * Returns the Unicode string length for the target UNC of
- * the specified entry by 'refno'
- *
- * Note that the UNC path should be encoded with ONE leading
- * slash not two as is common to user-visible UNC paths.
- */
-static uint16_t
-smb_dfs_referrals_unclen(dfs_info_t *referrals, uint16_t refno)
-{
- uint16_t len;
-
- if (refno >= referrals->i_ntargets)
- return (0);
-
- /* Encoded target UNC \server\share */
- len = smb_wcequiv_strlen(referrals->i_targets[refno].t_server) +
- smb_wcequiv_strlen(referrals->i_targets[refno].t_share) +
- smb_wcequiv_strlen("\\\\") + 2; /* two '\' + NULL */
-
- return (len);
+ return (SDRC_SUCCESS);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c
index 7fa91a0576..6efcbf7385 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c
@@ -22,7 +22,7 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
@@ -282,10 +282,11 @@ smb_sdrc_t
smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa)
{
int count;
- uint16_t sattr, odid;
+ uint16_t sattr;
smb_pathname_t *pn;
smb_odir_t *od;
smb_find_args_t args;
+ uint32_t status;
uint32_t odir_flags = 0;
bzero(&args, sizeof (smb_find_args_t));
@@ -324,16 +325,11 @@ smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa)
if (args.fa_maxdata == 0)
return (SDRC_ERROR);
- odid = smb_odir_open(sr, pn->pn_path, sattr, odir_flags);
- if (odid == 0) {
- if (sr->smb_error.status == NT_STATUS_OBJECT_PATH_NOT_FOUND) {
- smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
- ERRDOS, ERROR_FILE_NOT_FOUND);
- }
+ status = smb_odir_openpath(sr, pn->pn_path, sattr, odir_flags, &od);
+ if (status != 0) {
+ smbsr_error(sr, status, 0, 0);
return (SDRC_ERROR);
}
-
- od = smb_tree_lookup_odir(sr, odid);
if (od == NULL)
return (SDRC_ERROR);
@@ -357,15 +353,15 @@ smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa)
smb_odir_close(od);
} /* else leave odir open for trans2_find_next2 */
- smb_odir_release(od);
-
(void) smb_mbc_encodef(&xa->rep_param_mb, "wwwww",
- odid, /* Search ID */
+ od->d_odid, /* Search ID */
count, /* Search Count */
args.fa_eos, /* End Of Search */
0, /* EA Error Offset */
args.fa_lno); /* Last Name Offset */
+ smb_odir_release(od);
+
return (SDRC_SUCCESS);
}
@@ -540,6 +536,7 @@ smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od,
smb_odir_resume_t odir_resume;
uint16_t count, maxcount;
int rc = -1;
+ boolean_t need_rewind = B_FALSE;
if ((maxcount = args->fa_maxcount) == 0)
maxcount = 1;
@@ -549,17 +546,17 @@ smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od,
count = 0;
while (count < maxcount) {
- if (smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos)
- != 0)
- return (-1);
- if (args->fa_eos != 0)
+ rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
+ if (rc != 0 || args->fa_eos != 0)
break;
rc = smb_trans2_find_mbc_encode(sr, xa, &fileinfo, args);
if (rc == -1)
- return (-1);
- if (rc == 1)
- break;
+ return (-1); /* fatal encoding error */
+ if (rc == 1) {
+ need_rewind = B_TRUE;
+ break; /* output space exhausted */
+ }
/*
* Save the info about the last file returned.
@@ -569,6 +566,8 @@ smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od,
++count;
}
+ if (args->fa_eos != 0 && rc == ENOENT)
+ rc = 0;
/* save the last cookie returned to client */
if (count != 0)
@@ -579,8 +578,15 @@ smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od,
* and eos has not already been detected, check if there are
* any more entries. eos will be set if there are no more.
*/
- if ((rc == 0) && (args->fa_eos == 0))
- (void) smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
+ if ((rc == 0) && (args->fa_eos == 0)) {
+ rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
+ /*
+ * If rc == ENOENT, we did not read any additional data.
+ * if rc != 0, there's no need to rewind.
+ */
+ if (rc == 0)
+ need_rewind = B_TRUE;
+ }
/*
* When the last entry we read from the directory did not
@@ -589,10 +595,12 @@ smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od,
* check for EOS just above both can leave the directory
* position incorrect for the next call. Fix that now.
*/
- bzero(&odir_resume, sizeof (odir_resume));
- odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
- odir_resume.or_cookie = args->fa_lastkey;
- smb_odir_resume_at(od, &odir_resume);
+ if (need_rewind) {
+ bzero(&odir_resume, sizeof (odir_resume));
+ odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
+ odir_resume.or_cookie = args->fa_lastkey;
+ smb_odir_resume_at(od, &odir_resume);
+ }
return (count);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree.c b/usr/src/uts/common/fs/smbsrv/smb_tree.c
index 7ee4544475..3faf17ea20 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_tree.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_tree.c
@@ -171,15 +171,15 @@
int smb_tcon_mute = 0;
-static smb_tree_t *smb_tree_connect_core(smb_request_t *);
-static smb_tree_t *smb_tree_connect_disk(smb_request_t *, const char *);
-static smb_tree_t *smb_tree_connect_printq(smb_request_t *, const char *);
-static smb_tree_t *smb_tree_connect_ipc(smb_request_t *, const char *);
+uint32_t smb_tree_connect_core(smb_request_t *);
+uint32_t smb_tree_connect_disk(smb_request_t *, smb_arg_tcon_t *);
+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 boolean_t smb_tree_is_connected_locked(smb_tree_t *);
static boolean_t smb_tree_is_disconnected(smb_tree_t *);
-static const char *smb_tree_get_sharename(const char *);
+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 *);
static void smb_tree_get_flags(const smb_kshare_t *, vfs_t *, smb_tree_t *);
@@ -193,20 +193,19 @@ static int smb_tree_netinfo_encode(smb_tree_t *, uint8_t *, size_t, uint32_t *);
static void smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *);
static void smb_tree_netinfo_fini(smb_netconnectinfo_t *);
-smb_tree_t *
+uint32_t
smb_tree_connect(smb_request_t *sr)
{
- smb_tree_t *tree;
smb_server_t *sv = sr->sr_server;
+ uint32_t status;
if (smb_threshold_enter(&sv->sv_tcon_ct) != 0) {
- smbsr_error(sr, RPC_NT_SERVER_TOO_BUSY, 0, 0);
- return (NULL);
+ return (NT_STATUS_INSUFF_SERVER_RESOURCES);
}
- tree = smb_tree_connect_core(sr);
+ status = smb_tree_connect_core(sr);
smb_threshold_exit(&sv->sv_tcon_ct);
- return (tree);
+ return (status);
}
/*
@@ -221,56 +220,56 @@ smb_tree_connect(smb_request_t *sr)
* COMM Communications device
* ????? Any type of device (wildcard)
*/
-static smb_tree_t *
+uint32_t
smb_tree_connect_core(smb_request_t *sr)
{
- char *unc_path = sr->sr_tcon.path;
- smb_tree_t *tree = NULL;
+ smb_arg_tcon_t *tcon = &sr->sr_tcon;
smb_kshare_t *si;
- const char *name;
+ char *name;
+ uint32_t status;
- (void) smb_strlwr(unc_path);
+ (void) smb_strlwr(tcon->path);
- if ((name = smb_tree_get_sharename(unc_path)) == NULL) {
- smb_tree_log(sr, unc_path, "invalid UNC path");
- smbsr_error(sr, 0, ERRSRV, ERRinvnetname);
- return (NULL);
+ if ((name = smb_tree_get_sharename(tcon->path)) == NULL) {
+ smb_tree_log(sr, tcon->path, "invalid UNC path");
+ return (NT_STATUS_BAD_NETWORK_NAME);
}
si = smb_kshare_lookup(sr->sr_server, name);
if (si == NULL) {
smb_tree_log(sr, name, "share not found");
- smbsr_error(sr, 0, ERRSRV, ERRinvnetname);
- return (NULL);
+ return (NT_STATUS_BAD_NETWORK_NAME);
}
if (!strcasecmp(SMB_SHARE_PRINT, name)) {
smb_kshare_release(sr->sr_server, si);
smb_tree_log(sr, name, "access not permitted");
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
- return (NULL);
+ return (NT_STATUS_ACCESS_DENIED);
}
+ /* NB: name points into tcon->path - don't free it. */
+ tcon->name = name;
sr->sr_tcon.si = si;
switch (si->shr_type & STYPE_MASK) {
case STYPE_DISKTREE:
- tree = smb_tree_connect_disk(sr, name);
+ status = smb_tree_connect_disk(sr, &sr->sr_tcon);
break;
case STYPE_IPC:
- tree = smb_tree_connect_ipc(sr, name);
+ status = smb_tree_connect_ipc(sr, &sr->sr_tcon);
break;
case STYPE_PRINTQ:
- tree = smb_tree_connect_printq(sr, name);
+ status = smb_tree_connect_printq(sr, &sr->sr_tcon);
break;
default:
- smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE,
- ERRDOS, ERROR_BAD_DEV_TYPE);
+ status = NT_STATUS_BAD_DEVICE_TYPE;
break;
}
smb_kshare_release(sr->sr_server, si);
- return (tree);
+ sr->sr_tcon.si = NULL;
+
+ return (status);
}
/*
@@ -326,8 +325,7 @@ boolean_t
smb_tree_hold(
smb_tree_t *tree)
{
- ASSERT(tree);
- ASSERT(tree->t_magic == SMB_TREE_MAGIC);
+ SMB_TREE_VALID(tree);
mutex_enter(&tree->t_mutex);
@@ -342,6 +340,25 @@ smb_tree_hold(
}
/*
+ * Bump the hold count regardless of the tree state. This is used in
+ * some internal code paths where we've already checked that we had a
+ * valid tree connection, and don't want to deal with the possiblity
+ * that the tree state might have changed to disconnecting after our
+ * original hold was taken. It's correct to continue processing a
+ * request even when new requests cannot lookup that tree anymore.
+ */
+void
+smb_tree_hold_internal(
+ smb_tree_t *tree)
+{
+ SMB_TREE_VALID(tree);
+
+ mutex_enter(&tree->t_mutex);
+ tree->t_refcnt++;
+ mutex_exit(&tree->t_mutex);
+}
+
+/*
* Release a reference on a tree. If the tree is disconnected and the
* reference count falls to zero, post the object for deletion.
* Object deletion is deferred to avoid modifying a list while an
@@ -397,7 +414,7 @@ smb_tree_post_odir(smb_tree_t *tree, smb_odir_t *od)
void
smb_tree_close_pid(
smb_tree_t *tree,
- uint16_t pid)
+ uint32_t pid)
{
ASSERT(tree);
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
@@ -584,7 +601,7 @@ smb_tree_chkaccess(smb_request_t *sr, smb_kshare_t *shr, vnode_t *vp)
uint32_t acl_access;
uint32_t access;
- if (user->u_flags & SMB_USER_FLAG_IPC) {
+ if (user->u_flags & SMB_USER_FLAG_ANON) {
smb_tree_log(sr, sharename, "access denied: IPC only");
return (0);
}
@@ -624,15 +641,16 @@ smb_tree_chkaccess(smb_request_t *sr, smb_kshare_t *shr, vnode_t *vp)
/*
* Connect a share for use with files and directories.
*/
-static smb_tree_t *
-smb_tree_connect_disk(smb_request_t *sr, const char *sharename)
+uint32_t
+smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon)
{
+ char *sharename = tcon->path;
const char *any = "?????";
smb_user_t *user = sr->uid_user;
smb_node_t *dnode = NULL;
smb_node_t *snode = NULL;
- smb_kshare_t *si = sr->sr_tcon.si;
- char *service = sr->sr_tcon.service;
+ smb_kshare_t *si = tcon->si;
+ char *service = tcon->service;
char last_component[MAXNAMELEN];
smb_tree_t *tree;
int rc;
@@ -642,11 +660,11 @@ smb_tree_connect_disk(smb_request_t *sr, const char *sharename)
ASSERT(user);
ASSERT(user->u_cred);
- if ((strcmp(service, any) != 0) && (strcasecmp(service, "A:") != 0)) {
+ if (service != NULL &&
+ strcmp(service, any) != 0 &&
+ strcasecmp(service, "A:") != 0) {
smb_tree_log(sr, sharename, "invalid service (%s)", service);
- smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE,
- ERRDOS, ERROR_BAD_DEV_TYPE);
- return (NULL);
+ return (NT_STATUS_BAD_DEVICE_TYPE);
}
/*
@@ -654,7 +672,6 @@ smb_tree_connect_disk(smb_request_t *sr, const char *sharename)
*/
rc = smb_pathname_reduce(sr, user->u_cred, si->shr_path, 0, 0, &dnode,
last_component);
-
if (rc == 0) {
rc = smb_fsop_lookup(sr, user->u_cred, SMB_FOLLOW_LINKS,
sr->sr_server->si_root_smb_node, dnode, last_component,
@@ -668,30 +685,28 @@ smb_tree_connect_disk(smb_request_t *sr, const char *sharename)
smb_node_release(snode);
smb_tree_log(sr, sharename, "bad path: %s", si->shr_path);
- smbsr_error(sr, 0, ERRSRV, ERRinvnetname);
- return (NULL);
+ return (NT_STATUS_BAD_NETWORK_NAME);
}
if ((access = smb_tree_chkaccess(sr, si, snode->vp)) == 0) {
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
smb_node_release(snode);
- return (NULL);
+ return (NT_STATUS_ACCESS_DENIED);
}
/*
* Set up the OptionalSupport for this share.
*/
- sr->sr_tcon.optional_support = SMB_SUPPORT_SEARCH_BITS;
+ tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
switch (si->shr_flags & SMB_SHRF_CSC_MASK) {
case SMB_SHRF_CSC_DISABLED:
- sr->sr_tcon.optional_support |= SMB_CSC_CACHE_NONE;
+ tcon->optional_support |= SMB_CSC_CACHE_NONE;
break;
case SMB_SHRF_CSC_AUTO:
- sr->sr_tcon.optional_support |= SMB_CSC_CACHE_AUTO_REINT;
+ tcon->optional_support |= SMB_CSC_CACHE_AUTO_REINT;
break;
case SMB_SHRF_CSC_VDO:
- sr->sr_tcon.optional_support |= SMB_CSC_CACHE_VDO;
+ tcon->optional_support |= SMB_CSC_CACHE_VDO;
break;
case SMB_SHRF_CSC_MANUAL:
default:
@@ -703,11 +718,11 @@ smb_tree_connect_disk(smb_request_t *sr, const char *sharename)
/* ABE support */
if (si->shr_flags & SMB_SHRF_ABE)
- sr->sr_tcon.optional_support |=
+ tcon->optional_support |=
SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM;
if (si->shr_flags & SMB_SHRF_DFSROOT)
- sr->sr_tcon.optional_support |= SMB_SHARE_IS_IN_DFS;
+ tcon->optional_support |= SMB_SHARE_IS_IN_DFS;
/* if 'smb' zfs property: shortnames=disabled */
if (!smb_shortnames)
@@ -717,25 +732,25 @@ smb_tree_connect_disk(smb_request_t *sr, const char *sharename)
smb_node_release(snode);
- if (tree) {
- if (tree->t_execflags & SMB_EXEC_MAP) {
- smb_tree_set_execinfo(tree, &execinfo, SMB_EXEC_MAP);
+ if (tree == NULL)
+ return (NT_STATUS_INSUFF_SERVER_RESOURCES);
+
+ if (tree->t_execflags & SMB_EXEC_MAP) {
+ smb_tree_set_execinfo(tree, &execinfo, SMB_EXEC_MAP);
- rc = smb_kshare_exec(tree->t_server, &execinfo);
+ rc = smb_kshare_exec(tree->t_server, &execinfo);
- if ((rc != 0) && (tree->t_execflags & SMB_EXEC_TERM)) {
- smb_tree_disconnect(tree, B_FALSE);
- smb_tree_release(tree);
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV,
- ERRaccess);
- return (NULL);
- }
+ if ((rc != 0) && (tree->t_execflags & SMB_EXEC_TERM)) {
+ smb_tree_disconnect(tree, B_FALSE);
+ smb_tree_release(tree);
+ return (NT_STATUS_ACCESS_DENIED);
}
- } else {
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
}
- return (tree);
+ sr->tid_tree = tree;
+ sr->smb_tid = tree->t_tid;
+
+ return (0);
}
/*
@@ -744,15 +759,16 @@ smb_tree_connect_disk(smb_request_t *sr, const char *sharename)
* (permissions allowed by host based access) and aclaccess (from the
* share ACL).
*/
-static smb_tree_t *
-smb_tree_connect_printq(smb_request_t *sr, const char *sharename)
+uint32_t
+smb_tree_connect_printq(smb_request_t *sr, smb_arg_tcon_t *tcon)
{
+ char *sharename = tcon->path;
const char *any = "?????";
smb_user_t *user = sr->uid_user;
smb_node_t *dnode = NULL;
smb_node_t *snode = NULL;
- smb_kshare_t *si = sr->sr_tcon.si;
- char *service = sr->sr_tcon.service;
+ smb_kshare_t *si = tcon->si;
+ char *service = tcon->service;
char last_component[MAXNAMELEN];
smb_tree_t *tree;
int rc;
@@ -763,16 +779,14 @@ smb_tree_connect_printq(smb_request_t *sr, const char *sharename)
if (sr->sr_server->sv_cfg.skc_print_enable == 0) {
smb_tree_log(sr, sharename, "printing disabled");
- smbsr_error(sr, 0, ERRSRV, ERRinvnetname);
- return (NULL);
+ return (NT_STATUS_BAD_NETWORK_NAME);
}
- if ((strcmp(service, any) != 0) &&
- (strcasecmp(service, "LPT1:") != 0)) {
+ if (service != NULL &&
+ strcmp(service, any) != 0 &&
+ strcasecmp(service, "LPT1:") != 0) {
smb_tree_log(sr, sharename, "invalid service (%s)", service);
- smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE,
- ERRDOS, ERROR_BAD_DEV_TYPE);
- return (NULL);
+ return (NT_STATUS_BAD_DEVICE_TYPE);
}
/*
@@ -793,65 +807,67 @@ smb_tree_connect_printq(smb_request_t *sr, const char *sharename)
smb_node_release(snode);
smb_tree_log(sr, sharename, "bad path: %s", si->shr_path);
- smbsr_error(sr, 0, ERRSRV, ERRinvnetname);
- return (NULL);
+ return (NT_STATUS_BAD_NETWORK_NAME);
}
if ((access = smb_tree_chkaccess(sr, si, snode->vp)) == 0) {
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
smb_node_release(snode);
- return (NULL);
+ return (NT_STATUS_ACCESS_DENIED);
}
- sr->sr_tcon.optional_support = SMB_SUPPORT_SEARCH_BITS;
+ tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
tree = smb_tree_alloc(sr, si, snode, access, sr->sr_cfg->skc_execflags);
smb_node_release(snode);
if (tree == NULL)
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
+ return (NT_STATUS_INSUFF_SERVER_RESOURCES);
- return (tree);
+ sr->tid_tree = tree;
+ sr->smb_tid = tree->t_tid;
+
+ return (0);
}
/*
* Connect an IPC share for use with named pipes.
*/
-static smb_tree_t *
-smb_tree_connect_ipc(smb_request_t *sr, const char *name)
+uint32_t
+smb_tree_connect_ipc(smb_request_t *sr, smb_arg_tcon_t *tcon)
{
+ char *name = tcon->path;
const char *any = "?????";
smb_user_t *user = sr->uid_user;
smb_tree_t *tree;
- smb_kshare_t *si = sr->sr_tcon.si;
- char *service = sr->sr_tcon.service;
+ smb_kshare_t *si = tcon->si;
+ char *service = tcon->service;
ASSERT(user);
- if ((user->u_flags & SMB_USER_FLAG_IPC) &&
- sr->sr_cfg->skc_restrict_anon) {
- smb_tree_log(sr, name, "access denied: restrict anonymous");
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
- return (NULL);
+ if (service != NULL &&
+ strcmp(service, any) != 0 &&
+ strcasecmp(service, "IPC") != 0) {
+ smb_tree_log(sr, name, "invalid service (%s)", service);
+ return (NT_STATUS_BAD_DEVICE_TYPE);
}
- if ((strcmp(service, any) != 0) && (strcasecmp(service, "IPC") != 0)) {
- smb_tree_log(sr, name, "invalid service (%s)", service);
- smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE,
- ERRDOS, ERROR_BAD_DEV_TYPE);
- return (NULL);
+ if ((user->u_flags & SMB_USER_FLAG_ANON) &&
+ sr->sr_cfg->skc_restrict_anon) {
+ smb_tree_log(sr, name, "access denied: restrict anonymous");
+ return (NT_STATUS_ACCESS_DENIED);
}
- sr->sr_tcon.optional_support = SMB_SUPPORT_SEARCH_BITS;
+ tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
tree = smb_tree_alloc(sr, si, NULL, ACE_ALL_PERMS, 0);
- if (tree == NULL) {
- smb_tree_log(sr, name, "access denied");
- smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
- }
+ if (tree == NULL)
+ return (NT_STATUS_INSUFF_SERVER_RESOURCES);
- return (tree);
+ sr->tid_tree = tree;
+ sr->smb_tid = tree->t_tid;
+
+ return (0);
}
/*
@@ -1036,10 +1052,10 @@ smb_tree_is_disconnected(smb_tree_t *tree)
* (\\server\share) or simply the share name. We validate the UNC
* format but we don't look at the server name.
*/
-static const char *
-smb_tree_get_sharename(const char *unc_path)
+static char *
+smb_tree_get_sharename(char *unc_path)
{
- const char *sharename = unc_path;
+ char *sharename = unc_path;
if (sharename[0] == '\\') {
/*
@@ -1088,6 +1104,10 @@ smb_tree_getattr(const smb_kshare_t *si, smb_node_t *node, smb_tree_t *tree)
static void
smb_tree_get_volname(vfs_t *vfsp, smb_tree_t *tree)
{
+#ifdef _FAKE_KERNEL
+ _NOTE(ARGUNUSED(vfsp))
+ (void) strlcpy(tree->t_volume, "fake", SMB_VOLNAMELEN);
+#else /* _FAKE_KERNEL */
refstr_t *vfs_mntpoint;
const char *s;
char *name;
@@ -1102,9 +1122,11 @@ smb_tree_get_volname(vfs_t *vfsp, smb_tree_t *tree)
name = tree->t_volume;
(void) strsep((char **)&name, "/");
+#endif /* _FAKE_KERNEL */
}
/*
+ * Always set "unicode on disk" because we always use utf8 names locally.
* Always set ACL support because the VFS will fake ACLs for file systems
* that don't support them.
*
@@ -1124,15 +1146,16 @@ smb_tree_get_flags(const smb_kshare_t *si, vfs_t *vfsp, smb_tree_t *tree)
} smb_mtype_t;
static smb_mtype_t smb_mtype[] = {
- { "zfs", 3, SMB_TREE_UNICODE_ON_DISK |
- SMB_TREE_QUOTA | SMB_TREE_SPARSE},
- { "ufs", 3, SMB_TREE_UNICODE_ON_DISK },
+ { "zfs", 3, SMB_TREE_QUOTA | SMB_TREE_SPARSE},
+ { "ufs", 3, 0 },
{ "nfs", 3, SMB_TREE_NFS_MOUNTED },
{ "tmpfs", 5, SMB_TREE_NO_EXPORT }
};
smb_mtype_t *mtype;
char *name;
- uint32_t flags = SMB_TREE_SUPPORTS_ACLS;
+ uint32_t flags =
+ SMB_TREE_SUPPORTS_ACLS |
+ SMB_TREE_UNICODE_ON_DISK;
int i;
if (si->shr_flags & SMB_SHRF_DFSROOT)
diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree_connect.c b/usr/src/uts/common/fs/smbsrv/smb_tree_connect.c
index 19b857e834..f1bb880c43 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_tree_connect.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_tree_connect.c
@@ -20,12 +20,39 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_share.h>
+static void
+smb_tcon_puterror(smb_request_t *sr, uint32_t status)
+{
+
+ switch (status) {
+
+ case NT_STATUS_BAD_NETWORK_NAME:
+ /* Intentional status=0 */
+ smbsr_error(sr, 0, ERRSRV, ERRinvnetname);
+ break;
+
+ case NT_STATUS_ACCESS_DENIED:
+ smbsr_error(sr, status, ERRSRV, ERRaccess);
+ break;
+
+ case NT_STATUS_BAD_DEVICE_TYPE:
+ smbsr_error(sr, status, ERRDOS, ERROR_BAD_DEV_TYPE);
+ break;
+
+ default:
+ case NT_STATUS_INTERNAL_ERROR:
+ /* Intentional status=0 */
+ smbsr_error(sr, 0, ERRSRV, ERRsrverror);
+ break;
+ }
+}
+
/*
* SmbTreeConnect: Map a share to a tree and obtain a tree-id (TID).
*
@@ -88,14 +115,14 @@ smb_post_tree_connect(smb_request_t *sr)
smb_sdrc_t
smb_com_tree_connect(smb_request_t *sr)
{
- smb_tree_t *tree;
+ uint32_t status;
int rc;
- if ((tree = smb_tree_connect(sr)) == NULL)
+ status = smb_tree_connect(sr);
+ if (status) {
+ smb_tcon_puterror(sr, status);
return (SDRC_ERROR);
-
- sr->smb_tid = tree->t_tid;
- sr->tid_tree = tree;
+ }
rc = smbsr_encode_result(sr, 2, 0, "bwww",
2, /* wct */
@@ -280,17 +307,17 @@ smb_sdrc_t
smb_com_tree_connect_andx(smb_request_t *sr)
{
smb_arg_tcon_t *tcon = &sr->sr_tcon;
- smb_tree_t *tree;
char *service;
+ uint32_t status;
int rc;
- if ((tree = smb_tree_connect(sr)) == NULL)
+ status = smb_tree_connect(sr);
+ if (status) {
+ smb_tcon_puterror(sr, status);
return (SDRC_ERROR);
+ }
- sr->smb_tid = tree->t_tid;
- sr->tid_tree = tree;
-
- switch (tree->t_res_type & STYPE_MASK) {
+ switch (sr->tid_tree->t_res_type & STYPE_MASK) {
case STYPE_IPC:
service = "IPC";
break;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_vops.c b/usr/src/uts/common/fs/smbsrv/smb_vops.c
index f847053adc..fdcfeed19c 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_vops.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_vops.c
@@ -499,6 +499,17 @@ smb_vop_setattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *attr,
return (error);
}
+int
+smb_vop_space(vnode_t *vp, int cmd, flock64_t *bfp, int flags,
+ offset_t offset, cred_t *cr)
+{
+ int error;
+
+ error = VOP_SPACE(vp, cmd, bfp, flags, offset, cr, &smb_ct);
+
+ return (error);
+}
+
/*
* smb_vop_access
*
diff --git a/usr/src/uts/common/fs/smbsrv/smb_vss.c b/usr/src/uts/common/fs/smbsrv/smb_vss.c
index 6cf5fca371..211bc467a8 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_vss.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_vss.c
@@ -19,8 +19,8 @@
* CDDL HEADER END
*/
/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -52,17 +52,17 @@
static boolean_t smb_vss_is_gmttoken(const char *);
static const char *smb_vss_find_gmttoken(const char *);
-static uint32_t smb_vss_encode_gmttokens(smb_request_t *, smb_xa_t *,
+static uint32_t smb_vss_encode_gmttokens(smb_request_t *, smb_fsctl_t *,
int32_t, smb_gmttoken_response_t *);
static void smb_vss_remove_first_token_from_path(char *);
static uint32_t smb_vss_get_count(smb_tree_t *, char *);
-static void smb_vss_map_gmttoken(smb_tree_t *, char *, char *, char *);
+static void smb_vss_map_gmttoken(smb_tree_t *, char *, char *, time_t, char *);
static void smb_vss_get_snapshots(smb_tree_t *, char *,
uint32_t, smb_gmttoken_response_t *);
static void smb_vss_get_snapshots_free(smb_gmttoken_response_t *);
static int smb_vss_lookup_node(smb_request_t *sr, smb_node_t *, vnode_t *,
- char *, smb_node_t *, char *, smb_node_t **);
+ char *, smb_node_t *, smb_node_t **);
/*
* This is to respond to the nt_transact_ioctl to either respond with the
@@ -73,18 +73,18 @@ static int smb_vss_lookup_node(smb_request_t *sr, smb_node_t *, vnode_t *,
* snapshot).
*/
uint32_t
-smb_vss_ioctl_enumerate_snaps(smb_request_t *sr, smb_xa_t *xa)
+smb_vss_enum_snapshots(smb_request_t *sr, smb_fsctl_t *fsctl)
{
uint32_t count = 0;
char *root_path;
uint32_t status = NT_STATUS_SUCCESS;
smb_node_t *tnode;
- smb_gmttoken_response_t gmttokens;
+ smb_gmttoken_response_t snaps;
ASSERT(sr->tid_tree);
ASSERT(sr->tid_tree->t_snode);
- if (xa->smb_mdrcnt < SMB_VSS_COUNT_SIZE)
+ if (fsctl->MaxOutputResp < SMB_VSS_COUNT_SIZE)
return (NT_STATUS_INVALID_PARAMETER);
tnode = sr->tid_tree->t_snode;
@@ -92,22 +92,22 @@ smb_vss_ioctl_enumerate_snaps(smb_request_t *sr, smb_xa_t *xa)
if (smb_node_getmntpath(tnode, root_path, MAXPATHLEN) != 0)
return (NT_STATUS_INVALID_PARAMETER);
- if (xa->smb_mdrcnt == SMB_VSS_COUNT_SIZE) {
+ if (fsctl->MaxOutputResp == SMB_VSS_COUNT_SIZE) {
count = smb_vss_get_count(sr->tid_tree, root_path);
- if (smb_mbc_encodef(&xa->rep_data_mb, "lllw", count, 0,
+ if (smb_mbc_encodef(fsctl->out_mbc, "lllw", count, 0,
(count * SMB_VSS_GMT_NET_SIZE(sr) +
smb_ascii_or_unicode_null_len(sr)), 0) != 0) {
status = NT_STATUS_INVALID_PARAMETER;
}
} else {
- count = xa->smb_mdrcnt / SMB_VSS_GMT_NET_SIZE(sr);
+ count = fsctl->MaxOutputResp / SMB_VSS_GMT_NET_SIZE(sr);
smb_vss_get_snapshots(sr->tid_tree, root_path,
- count, &gmttokens);
+ count, &snaps);
- status = smb_vss_encode_gmttokens(sr, xa, count, &gmttokens);
+ status = smb_vss_encode_gmttokens(sr, fsctl, count, &snaps);
- smb_vss_get_snapshots_free(&gmttokens);
+ smb_vss_get_snapshots_free(&snaps);
}
kmem_free(root_path, MAXPATHLEN);
@@ -142,12 +142,15 @@ smb_vss_lookup_nodes(smb_request_t *sr, smb_node_t *root_node,
smb_node_t *cur_node, char *buf, smb_node_t **vss_cur_node,
smb_node_t **vss_root_node)
{
- const char *p;
+ smb_arg_open_t *op = &sr->arg.open;
smb_node_t *tnode;
char *snapname, *path;
- char gmttoken[SMB_VSS_GMT_SIZE];
+ char *gmttoken;
+ char gmttok_buf[SMB_VSS_GMT_SIZE];
vnode_t *fsrootvp = NULL;
+ time_t toktime;
int err = 0;
+ boolean_t smb1;
if (sr->tid_tree == NULL)
return (ESTALE);
@@ -158,12 +161,23 @@ smb_vss_lookup_nodes(smb_request_t *sr, smb_node_t *root_node,
ASSERT(tnode->vp);
ASSERT(tnode->vp->v_vfsp);
- /* get gmttoken from buf and find corresponding snapshot name */
- if ((p = smb_vss_find_gmttoken(buf)) == NULL)
- return (ENOENT);
+ smb1 = (sr->session->dialect < SMB_VERS_2_BASE);
+ if (smb1) {
+ const char *p;
- bcopy(p, gmttoken, SMB_VSS_GMT_SIZE);
- gmttoken[SMB_VSS_GMT_SIZE - 1] = '\0';
+ /* get gmttoken from buf */
+ if ((p = smb_vss_find_gmttoken(buf)) == NULL)
+ return (ENOENT);
+
+ bcopy(p, gmttok_buf, SMB_VSS_GMT_SIZE);
+ gmttok_buf[SMB_VSS_GMT_SIZE - 1] = '\0';
+ gmttoken = gmttok_buf;
+ toktime = 0;
+ } else {
+ /* SMB2 and later */
+ gmttoken = NULL;
+ toktime = op->timewarp.tv_sec;
+ }
path = smb_srm_alloc(sr, MAXPATHLEN);
snapname = smb_srm_alloc(sr, MAXPATHLEN);
@@ -172,9 +186,14 @@ smb_vss_lookup_nodes(smb_request_t *sr, smb_node_t *root_node,
if (err != 0)
return (err);
+ /*
+ * Find the corresponding snapshot name. If snapname is
+ * empty after the map call, no such snapshot was found.
+ */
*snapname = '\0';
- smb_vss_map_gmttoken(sr->tid_tree, path, gmttoken, snapname);
- if (!*snapname)
+ smb_vss_map_gmttoken(sr->tid_tree, path, gmttoken, toktime,
+ snapname);
+ if (*snapname == '\0')
return (ENOENT);
/* find snapshot nodes */
@@ -184,18 +203,20 @@ smb_vss_lookup_nodes(smb_request_t *sr, smb_node_t *root_node,
/* find snapshot node corresponding to root_node */
err = smb_vss_lookup_node(sr, root_node, fsrootvp,
- snapname, cur_node, gmttoken, vss_root_node);
+ snapname, cur_node, vss_root_node);
if (err == 0) {
/* find snapshot node corresponding to cur_node */
err = smb_vss_lookup_node(sr, cur_node, fsrootvp,
- snapname, cur_node, gmttoken, vss_cur_node);
+ snapname, cur_node, vss_cur_node);
if (err != 0)
smb_node_release(*vss_root_node);
}
VN_RELE(fsrootvp);
- smb_vss_remove_first_token_from_path(buf);
+ if (smb1)
+ smb_vss_remove_first_token_from_path(buf);
+
return (err);
}
@@ -208,7 +229,7 @@ smb_vss_lookup_nodes(smb_request_t *sr, smb_node_t *root_node,
*/
static int
smb_vss_lookup_node(smb_request_t *sr, smb_node_t *node, vnode_t *fsrootvp,
- char *snapname, smb_node_t *dnode, char *odname, smb_node_t **vss_node)
+ char *snapname, smb_node_t *dnode, smb_node_t **vss_node)
{
char *p, *path;
int err, len;
@@ -226,7 +247,7 @@ smb_vss_lookup_node(smb_request_t *sr, smb_node_t *node, vnode_t *fsrootvp,
vp = smb_lookuppathvptovp(sr, path, fsrootvp, fsrootvp);
if (vp) {
*vss_node = smb_node_lookup(sr, NULL, zone_kcred(),
- vp, odname, dnode, NULL);
+ vp, snapname, dnode, NULL);
VN_RELE(vp);
}
}
@@ -277,7 +298,7 @@ smb_vss_find_gmttoken(const char *path)
p = path;
while (*p) {
- if (smb_vss_is_gmttoken(p))
+ if (*p == '@' && smb_vss_is_gmttoken(p))
return (p);
p++;
}
@@ -285,7 +306,7 @@ smb_vss_find_gmttoken(const char *path)
}
static uint32_t
-smb_vss_encode_gmttokens(smb_request_t *sr, smb_xa_t *xa,
+smb_vss_encode_gmttokens(smb_request_t *sr, smb_fsctl_t *fsctl,
int32_t count, smb_gmttoken_response_t *snap_data)
{
uint32_t i;
@@ -305,13 +326,13 @@ smb_vss_encode_gmttokens(smb_request_t *sr, smb_xa_t *xa,
data_size = returned_count * SMB_VSS_GMT_NET_SIZE(sr) +
smb_ascii_or_unicode_null_len(sr);
- if (smb_mbc_encodef(&xa->rep_data_mb, "lll", returned_count,
+ if (smb_mbc_encodef(fsctl->out_mbc, "lll", returned_count,
num_gmttokens, data_size) != 0)
return (NT_STATUS_INVALID_PARAMETER);
if (status == NT_STATUS_SUCCESS) {
for (i = 0; i < num_gmttokens; i++) {
- if (smb_mbc_encodef(&xa->rep_data_mb, "%u", sr,
+ if (smb_mbc_encodef(fsctl->out_mbc, "%u", sr,
*gmttokens) != 0)
status = NT_STATUS_INVALID_PARAMETER;
gmttokens++;
@@ -405,7 +426,7 @@ smb_vss_get_snapshots_free(smb_gmttoken_response_t *reply)
*/
static void
smb_vss_map_gmttoken(smb_tree_t *tree, char *path, char *gmttoken,
- char *snapname)
+ time_t toktime, char *snapname)
{
smb_gmttoken_snapname_t request;
smb_string_t result;
@@ -415,6 +436,7 @@ smb_vss_map_gmttoken(smb_tree_t *tree, char *path, char *gmttoken,
request.gts_path = path;
request.gts_gmttoken = gmttoken;
+ request.gts_toktime = toktime;
(void) smb_kdoor_upcall(tree->t_server, SMB_DR_VSS_MAP_GMTTOKEN,
&request, smb_gmttoken_snapname_xdr,
diff --git a/usr/src/uts/common/smb/ntstatus.h b/usr/src/uts/common/smb/ntstatus.h
index 12ccc263a4..643cab3a4d 100644
--- a/usr/src/uts/common/smb/ntstatus.h
+++ b/usr/src/uts/common/smb/ntstatus.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SMB_NTSTATUS_H
@@ -631,6 +632,7 @@ extern "C" {
#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT 0xC000019A
#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT 0xC000019B
#define NT_STATUS_FS_DRIVER_REQUIRED 0xC000019C
+#define NT_STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME 0xC000019F
#define NT_STATUS_NO_USER_SESSION_KEY 0xC0000202
#define NT_STATUS_USER_SESSION_DELETED 0xC0000203
#define NT_STATUS_RESOURCE_LANG_NOT_FOUND 0xC0000204
diff --git a/usr/src/uts/common/smbsrv/Makefile b/usr/src/uts/common/smbsrv/Makefile
index aaaee2bd93..f48dc89acb 100644
--- a/usr/src/uts/common/smbsrv/Makefile
+++ b/usr/src/uts/common/smbsrv/Makefile
@@ -58,6 +58,8 @@ HDRS= alloc.h \
smb_vops.h \
smb_xdr.h \
smbinfo.h \
+ smb2.h \
+ smb2_kproto.h \
string.h \
svrapi.h \
winioctl.h \
diff --git a/usr/src/uts/common/smbsrv/mbuf.h b/usr/src/uts/common/smbsrv/mbuf.h
index 4b66be8715..0bec3a1a5a 100644
--- a/usr/src/uts/common/smbsrv/mbuf.h
+++ b/usr/src/uts/common/smbsrv/mbuf.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -61,28 +61,20 @@
#define _SMBSRV_MBUF_H
/*
- * PBSHORTCUT This file should be removed from the PB port but is required
- * for now to get it to compile. This file has also been modified.
+ * This mbuf simulation should be replaced with (native) mblk_t support.
*/
#include <sys/types.h>
#include <sys/param.h>
-#include <sys/list.h>
+#include <sys/kmem.h>
#include <smbsrv/string.h>
-#include <smbsrv/alloc.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MSIZE 256
-#define MCLBYTES 2048
-#define MCLSHIFT 11
-#define MCLOFSET (MCLBYTES - 1)
-
-#define NBPG 4096
-#define CLBYTES NBPG
-#define PB_PAGESIZE 4096
+#define MCLBYTES 8192
/*
* Mbufs are of a single size, MSIZE (machine/machparam.h), which
@@ -120,8 +112,6 @@ struct pkthdr {
};
-/* XXX probably do not need m_ext */
-
/* description of external storage mapped into mbuf, valid if M_EXT set */
struct m_ext {
caddr_t ext_buf; /* start of buffer */
@@ -209,7 +199,7 @@ typedef struct mbuf {
*/
#define MGET(m, how, type) { \
- m = MEM_ZALLOC("mbuf", sizeof (struct mbuf)); \
+ m = smb_mbuf_alloc(); \
(m)->m_next = (struct mbuf *)NULL; \
(m)->m_nextpkt = (struct mbuf *)NULL; \
(m)->m_data = (m)->m_dat; \
@@ -218,7 +208,7 @@ typedef struct mbuf {
}
#define MGETHDR(m, how, type) { \
- m = MEM_ZALLOC("mbuf", sizeof (struct mbuf)); \
+ m = smb_mbuf_alloc(); \
(m)->m_type = (MT_HEADER); \
(m)->m_next = (struct mbuf *)NULL; \
(m)->m_nextpkt = (struct mbuf *)NULL; \
@@ -226,15 +216,13 @@ typedef struct mbuf {
(m)->m_flags = M_PKTHDR; \
}
-extern int mclref();
-extern int mclrefnoop();
#define MCLGET(m, how) \
{ \
- (m)->m_ext.ext_buf = MEM_ZALLOC("mbuf", MCLBYTES); \
+ (m)->m_ext.ext_buf = smb_mbufcl_alloc(); \
(m)->m_data = (m)->m_ext.ext_buf; \
(m)->m_flags |= M_EXT; \
(m)->m_ext.ext_size = MCLBYTES; \
- (m)->m_ext.ext_ref = mclref; \
+ (m)->m_ext.ext_ref = smb_mbufcl_ref; \
}
/*
@@ -251,7 +239,7 @@ extern int mclrefnoop();
} \
(nn) = (m)->m_next; \
(m)->m_next = 0; \
- MEM_FREE("mbuf", m); \
+ smb_mbuf_free(m); \
}
@@ -268,7 +256,6 @@ extern int mclrefnoop();
typedef struct mbuf_chain {
uint32_t mbc_magic;
- list_node_t mbc_lnd;
volatile uint32_t flags; /* Various flags */
struct mbuf_chain *shadow_of; /* I'm shadowing someone */
mbuf_t *chain; /* Start of chain */
@@ -276,9 +263,15 @@ typedef struct mbuf_chain {
int32_t chain_offset; /* Current offset into chain */
} mbuf_chain_t;
+mbuf_t *smb_mbuf_alloc(void);
+void smb_mbuf_free(mbuf_t *);
+
+void *smb_mbufcl_alloc(void);
+void smb_mbufcl_free(void *);
+int smb_mbufcl_ref(void *, uint_t, int);
+
mbuf_t *m_free(mbuf_t *);
void m_freem(mbuf_t *);
-int mbc_moveout(mbuf_chain_t *, caddr_t, int, int *);
void smb_mbc_init(void);
void smb_mbc_fini(void);
mbuf_chain_t *smb_mbc_alloc(uint32_t);
diff --git a/usr/src/uts/common/smbsrv/netbios.h b/usr/src/uts/common/smbsrv/netbios.h
index 4b1f7814e0..de1740a360 100644
--- a/usr/src/uts/common/smbsrv/netbios.h
+++ b/usr/src/uts/common/smbsrv/netbios.h
@@ -136,13 +136,6 @@ extern "C" {
#define DEFAULT_TTL (600 * SECONDS)
#define SSN_RETRY_COUNT 4
#define SSN_CLOSE_TIMEOUT (30 * SECONDS)
-/*
- * K.L. The keep alive time out use to be default to
- * 900 seconds. It is not long enough for some applications
- * i.e. MS Access. Therefore, the timeout is increased to
- * 5400 seconds.
- */
-#define SSN_KEEP_ALIVE_TIMEOUT (90 * 60) /* seconds */
#define FRAGMENT_TIMEOUT (2 * SECONDS)
/* smb_netbios_util.c */
diff --git a/usr/src/uts/common/smbsrv/smb.h b/usr/src/uts/common/smbsrv/smb.h
index 9c421bc454..a4986bea78 100644
--- a/usr/src/uts/common/smbsrv/smb.h
+++ b/usr/src/uts/common/smbsrv/smb.h
@@ -20,7 +20,6 @@
*/
/*
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
@@ -127,6 +126,7 @@ typedef struct smb_hdrbuf {
*/
#define SMB_PROTOCOL_MAGIC 0x424d53ff
+#define SMB2_PROTOCOL_MAGIC 0x424d53fe
/*
* Time and date encoding (CIFS Section 3.6). The date is encoded such
@@ -234,6 +234,17 @@ typedef uint32_t smb_utime_t;
#define FILE_ACTION_ADDED_STREAM 0x00000006
#define FILE_ACTION_REMOVED_STREAM 0x00000007
#define FILE_ACTION_MODIFIED_STREAM 0x00000008
+/*
+ * Note: These action values are not from MS-FSCC.
+ * FILE_ACTION_SUBDIR_CHANGED is used internally for
+ * "watch tree" support, posted to all parents of a
+ * directory that had one of the changes above.
+ * FILE_ACTION_DELETE_PENDING is used internally to tell
+ * notify change requests when the "delete-on-close" flag
+ * has been set on the directory being watched.
+ */
+#define FILE_ACTION_SUBDIR_CHANGED 0x00000009
+#define FILE_ACTION_DELETE_PENDING 0x0000000a
/* Lock type flags */
@@ -402,6 +413,8 @@ typedef uint32_t smb_utime_t;
#define LANMAN2_1 9 /* OS/2 LANMAN2.1 */
#define Windows_for_Workgroups_3_1a 10 /* Windows for Workgroups Version 1.0 */
#define NT_LM_0_12 11 /* The SMB protocol designed for NT */
+#define DIALECT_SMB2002 12 /* SMB 2.002 (switch to SMB2) */
+#define DIALECT_SMB2XXX 13 /* SMB 2.??? (switch to SMB2) */
/*
* SMB_TREE_CONNECT_ANDX OptionalSupport flags
@@ -924,6 +937,20 @@ typedef uint32_t smb_utime_t;
#define FILE_VIRTUAL_VOLUME 0x00000040
/*
+ * File System Control Flags for smb_com_trans2_query|set_fs_information
+ * level SMB_FILE_FS_CONTROL_INFORMATION
+ */
+#define FILE_VC_QUOTA_TRACK 0x00000001
+#define FILE_VC_QUOTA_ENFORCE 0x00000002
+#define FILE_VC_CONTENT_INDEX_DISABLED 0x00000008
+#define FILE_VC_LOG_QUOTA_THRESHOLD 0x00000010
+#define FILE_VC_LOG_QUOTA_LIMIT 0x00000020
+#define FILE_VC_LOG_VOLUME_THRESHOLD 0x00000040
+#define FILE_VC_LOG_VOLUME_LIMIT 0x00000080
+#define FILE_VC_QUOTAS_INCOMPLETE 0x00000100
+#define FILE_VC_QUOTAS_REBUILDING 0x00000200
+
+/*
* CREATE_ANDX ShareAccess Flags
*/
diff --git a/usr/src/uts/common/smbsrv/smb2.h b/usr/src/uts/common/smbsrv/smb2.h
new file mode 100644
index 0000000000..c072f8233c
--- /dev/null
+++ b/usr/src/uts/common/smbsrv/smb2.h
@@ -0,0 +1,371 @@
+/*
+ * 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 2015 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#ifndef _SMB_SMB2_H
+#define _SMB_SMB2_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SMB2_PROTOCOL_ID { 0xFE, 'S', 'M', 'B' }
+#define SMB2_HDR_SIZE 64
+
+/*
+ * SMB2 header command codes.
+ * These are uint16_t on the wire.
+ */
+typedef enum {
+ SMB2_NEGOTIATE = 0,
+ SMB2_SESSION_SETUP,
+ SMB2_LOGOFF,
+ SMB2_TREE_CONNECT,
+ SMB2_TREE_DISCONNECT,
+ SMB2_CREATE,
+ SMB2_CLOSE,
+ SMB2_FLUSH,
+ SMB2_READ,
+ SMB2_WRITE,
+ SMB2_LOCK,
+ SMB2_IOCTL,
+ SMB2_CANCEL,
+ SMB2_ECHO,
+ SMB2_QUERY_DIRECTORY,
+ SMB2_CHANGE_NOTIFY,
+ SMB2_QUERY_INFO,
+ SMB2_SET_INFO,
+ SMB2_OPLOCK_BREAK,
+ /*
+ * The above (oplock break) is the last real SMB2 op-code.
+ * We use one more slot to represent invalid commands, and
+ * the final enum value is used for array sizes. Keep last!
+ */
+ SMB2_INVALID_CMD,
+ SMB2__NCMDS
+} SMB2_cmd_code;
+
+/*
+ * SMB2 header flags.
+ */
+
+/*
+ * SERVER_TO_REDIR
+ * When set, indicates the message is a response rather than
+ * a request. This MUST be set on responses sent from the
+ * server to the client, and MUST NOT be set on requests
+ * sent from the client to the server.
+ */
+#define SMB2_FLAGS_SERVER_TO_REDIR 0x00000001
+
+/*
+ * ASYNC_COMMAND
+ * When set, indicates that this is an ASYNC SMB2 header.
+ * Always set for headers of the form described in this
+ * section.
+ */
+#define SMB2_FLAGS_ASYNC_COMMAND 0x00000002
+
+/*
+ * RELATED_OPERATIONS
+ * When set in an SMB2 request, indicates that this request
+ * is a related operation in a compounded request chain.
+ * [MS-SMB2 sec. 3.2.4.1.4]
+ *
+ * When set in an SMB2 compound response, indicates that
+ * the request corresponding to this response was part of a
+ * related operation in a compounded request chain.
+ * [MS-SMB2 sec. 3.3.5.2.7.2]
+ */
+#define SMB2_FLAGS_RELATED_OPERATIONS 0x00000004
+
+/*
+ * SIGNED
+ * When set, indicates that this packet has been signed.
+ * [MS-SMB2 3.1.5.1]
+ */
+#define SMB2_FLAGS_SIGNED 0x00000008
+
+/*
+ * [MS-SMB2] 3.2.5.3.1 The SessionKey MUST be set to the
+ * first 16 bytes of the cryptographic key from GSSAPI.
+ * (Padded with zeros if the GSSAPI key is shorter.)
+ */
+#define SMB2_SESSION_KEY_LEN 16
+
+/*
+ * DFS_OPERATIONS
+ * When set, indicates that this command is a Distributed
+ * File System (DFS) operation. [MS-SMB2 3.3.5.9]
+ */
+#define SMB2_FLAGS_DFS_OPERATIONS 0x10000000
+
+/*
+ * REPLAY_OPERATION
+ * This flag is only valid for the SMB 3.0 dialect. When set,
+ * it indicates that this command is a replay operation.
+ * The client MUST ignore this bit on receipt.
+ */
+#define SMB2_FLAGS_REPLAY_OPERATION 0x20000000
+
+/*
+ * SMB2 Netgotiate [MS-SMB2 2.2.3]
+ */
+
+#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x01
+#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x02
+
+#define SMB2_CAP_DFS 0x00000001
+
+/* Added with SMB2.1 */
+#define SMB2_CAP_DFS 0x00000001
+#define SMB2_CAP_LEASING 0x00000002
+/*
+ * LARGE_MTU:
+ * When set, indicates that the client supports multi-credit operations.
+ */
+#define SMB2_CAP_LARGE_MTU 0x00000004
+
+/* Added with SMB3.0 */
+#define SMB2_CAP_MULTI_CHANNEL 0x00000008
+#define SMB2_CAP_PERSISTENT_HANDLES 0x00000010
+#define SMB2_CAP_DIRECTORY_LEASING 0x00000020
+#define SMB2_CAP_ENCRYPTION 0x00000040
+
+/* SMB2 session flags */
+#define SMB2_SESSION_FLAG_IS_GUEST 0x0001
+#define SMB2_SESSION_FLAG_IS_NULL 0x0002
+#define SMB2_SESSION_FLAG_ENCRYPT_DATA 0x0004
+
+/*
+ * SMB2 Tree connect, disconnect
+ */
+
+/* SMB2 sharetype flags */
+#define SMB2_SHARE_TYPE_DISK 0x1
+#define SMB2_SHARE_TYPE_PIPE 0x2
+#define SMB2_SHARE_TYPE_PRINT 0x3
+
+/* SMB2 share flags */
+#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000
+#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010
+#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020
+#define SMB2_SHAREFLAG_NO_CACHING 0x00000030
+#define SMB2_SHAREFLAG_DFS 0x00000001
+#define SMB2_SHAREFLAG_DFS_ROOT 0x00000002
+#define SMB2_SHAREFLAG_RESTRICT_EXCLUSIVE_OPENS 0x00000100
+#define SMB2_SHAREFLAG_FORCE_SHARED_DELETE 0x00000200
+#define SMB2_SHAREFLAG_ALLOW_NAMESPACE_CACHING 0x00000400
+#define SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM 0x00000800
+#define SMB2_SHAREFLAG_FORCE_LEVELII_OPLOCK 0x00001000
+/* SMB 3.0 */
+#define SMB2_SHAREFLAG_ENABLE_HASH_V1 0x00002000
+#define SMB2_SHAREFLAG_ENABLE_HASH_V2 0x00004000
+#define SMB2_SHAREFLAG_ENCRYPT_DATA 0x00008000
+
+/* SMB2 share capabilities */
+#define SMB2_SHARE_CAP_DFS 0x00000008
+/* SMB 3.0 */
+#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY 0x00000010
+#define SMB2_SHARE_CAP_SCALEOUT 0x00000020
+#define SMB2_SHARE_CAP_CLUSTER 0x00000040
+
+/*
+ * SMB2 Create (open)
+ */
+
+/* SMB2 requested oplock levels */
+#define SMB2_OPLOCK_LEVEL_NONE 0x00
+#define SMB2_OPLOCK_LEVEL_II 0x01
+#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
+#define SMB2_OPLOCK_LEVEL_BATCH 0x09
+#define SMB2_OPLOCK_LEVEL_LEASE 0xFF
+
+/* SMB2 impersonation levels */
+#define SMB2_IMPERSONATION_ANONYMOUS 0x00
+#define SMB2_IMPERSONATION_IDENTIFICATION 0x01
+#define SMB2_IMPERSONATION_IMPERSONATION 0x02
+#define SMB2_IMPERSONATION_DELEGATE 0x03
+
+/*
+ * Note: ShareAccess, CreateDispositon, CreateOptions,
+ * all use the same definitions as SMB1 (from MS-FSA).
+ * Ditto FileAccess flags (as with ACLs)
+ */
+
+/* SMB2 Create Context tags */
+
+#define SMB2_CREATE_EA_BUFFER 0x45787441 /* ("ExtA") */
+/*
+ * The data contains the extended attributes
+ * that MUST be stored on the created file.
+ * This value MUST NOT be set for named
+ * pipes and print files.
+ */
+
+#define SMB2_CREATE_SD_BUFFER 0x53656344 /* ("SecD") */
+/*
+ * The data contains a security descriptor that
+ * MUST be stored on the created file.
+ * This value MUST NOT be set for named
+ * pipes and print files.
+ */
+
+#define SMB2_CREATE_DURABLE_HANDLE_REQUEST 0x44486e51 /* ("DHnQ") */
+/* The client is requesting the open to be durable */
+
+#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT 0x44486e43 /* ("DHnC") */
+/*
+ * The client is requesting to reconnect to a
+ * durable open after being disconnected
+ */
+
+#define SMB2_CREATE_ALLOCATION_SIZE 0x416c5369 /* ("AISi") */
+/*
+ * The data contains the required allocation
+ * size of the newly created file.
+ */
+
+#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQ 0x4d784163 /* ("MxAc") */
+/*
+ * The client is requesting that the server
+ * return maximal access information.
+ */
+
+#define SMB2_CREATE_TIMEWARP_TOKEN 0x54577270 /* ("TWrp") */
+/*
+ * The client is requesting that the server
+ * open an earlier version of the file identified
+ * by the provided time stamp.
+ */
+
+#define SMB2_CREATE_QUERY_ON_DISK_ID 0x51466964 /* ("QFid") */
+/*
+ * The client is requesting that the server return a 32-byte
+ * opaque BLOB that uniquely identifies the file being opened
+ * on disk. No data is passed to the server by the client.
+ */
+
+#define SMB2_CREATE_REQUEST_LEASE 0x52714c73 /* ("RqLs") */
+/*
+ * The client is requesting that the server return a lease.
+ * This value is only supported for the SMB 2.1 and 3.0 dialects.
+ */
+
+/* SMB2 create request lease */
+#define SMB2_LEASE_NONE 0x00
+#define SMB2_LEASE_READ_CACHING 0x01
+#define SMB2_LEASE_HANDLE_CACHING 0x02
+#define SMB2_LEASE_WRITE_CACHING 0x04
+
+/* SMB2 lease break notification flags */
+#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED 0x01
+
+/*
+ * SMB2 Close
+ */
+#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB 0x0001
+
+/*
+ * SMB2 Write
+ */
+#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001
+
+/*
+ * SMB2 Lock Request
+ */
+
+/* SMB2 lock flags */
+
+/*
+ * SMB2_LOCKFLAG_SHARED_LOCK
+ * The range MUST be locked shared, allowing other opens
+ * to read from or take a shared lock on the range. All opens
+ * MUST NOT be allowed to write within the range. Other
+ * locks can be requested and taken on this range.
+ */
+#define SMB2_LOCKFLAG_SHARED_LOCK 0x00000001
+
+/*
+ * SMB2_LOCKFLAG_EXCLUSIVE_LOCK
+ * The range MUST be locked exclusive, not allowing other
+ * opens to read, write, or lock within the range.
+ */
+#define SMB2_LOCKFLAG_EXCLUSIVE_LOCK 0x00000002
+
+/*
+ * SMB2_LOCKFLAG_UNLOCK
+ * The range MUST be unlocked from a previous lock taken
+ * on this range. The unlock range MUST be identical to the
+ * lock range. Sub-ranges cannot be unlocked.
+ */
+#define SMB2_LOCKFLAG_UNLOCK 0x00000004
+
+/*
+ * SMB2_LOCKFLAG_FAIL_IMMEDIATELY
+ * The lock operation MUST fail immediately if it conflicts
+ * with an existing lock, instead of waiting for the range to
+ * become available. This can be OR'ed with either of
+ * shared_lock, exclusive_lock (nothing else).
+ */
+#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x00000010
+
+/*
+ * SMB2 Ioctl Request
+ */
+#define SMB2_0_IOCTL_IS_FSCTL 0x00000001
+
+
+/*
+ * SMB2 Query Directory
+ */
+
+/*
+ * SMB2 query directory info levels
+ * Same as SMB1 (see ntifs.h)
+ */
+
+/*
+ * SMB2 Query Directory Flags
+ * (our own names for these - spec. used poor names)
+ */
+#define SMB2_QDIR_FLAG_RESTART 0x01 /* SMB2_RESTART_SCANS */
+#define SMB2_QDIR_FLAG_SINGLE 0x02 /* SMB2_RETURN_SINGLE_ENTRY */
+#define SMB2_QDIR_FLAG_INDEX 0x04 /* SMB2_INDEX_SPECIFIED */
+#define SMB2_QDIR_FLAG_REOPEN 0x10 /* SMB2_REOPEN */
+
+/*
+ * SMB2 Query Info Request
+ */
+
+/* info type */
+#define SMB2_0_INFO_FILE 0x01
+/* The file information is requested. */
+#define SMB2_0_INFO_FILESYSTEM 0x02
+/* The underlying object store information is requested. */
+#define SMB2_0_INFO_SECURITY 0x03
+/* The security information is requested. */
+#define SMB2_0_INFO_QUOTA 0x04
+/* The underlying object store quota information is requested. */
+
+/*
+ * SMB2 Change Nofity Request
+ */
+#define SMB2_WATCH_TREE 0x00000001
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMB_SMB2_H */
diff --git a/usr/src/uts/common/smbsrv/smb2_kproto.h b/usr/src/uts/common/smbsrv/smb2_kproto.h
new file mode 100644
index 0000000000..17ac24ed36
--- /dev/null
+++ b/usr/src/uts/common/smbsrv/smb2_kproto.h
@@ -0,0 +1,95 @@
+/*
+ * 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 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#ifndef _SMB2_KPROTO_H_
+#define _SMB2_KPROTO_H_
+
+#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb2.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern uint32_t smb2_tcp_rcvbuf;
+extern uint32_t smb2_max_rwsize;
+extern uint32_t smb2_max_trans;
+
+void smb2_dispatch_stats_init(smb_server_t *);
+void smb2_dispatch_stats_fini(smb_server_t *);
+void smb2_dispatch_stats_update(smb_server_t *,
+ smb_kstat_req_t *, int, int);
+
+int smb2sr_newrq(smb_request_t *);
+int smb2sr_newrq_async(smb_request_t *);
+int smb2sr_newrq_cancel(smb_request_t *);
+void smb2sr_work(smb_request_t *);
+
+int smb2_decode_header(smb_request_t *);
+int smb2_encode_header(smb_request_t *, boolean_t);
+void smb2_send_reply(smb_request_t *);
+void smb2sr_put_error(smb_request_t *, uint32_t);
+void smb2sr_put_error_data(smb_request_t *, uint32_t, mbuf_chain_t *);
+void smb2sr_put_errno(struct smb_request *, int);
+uint32_t smb2sr_lookup_fid(smb_request_t *, smb2fid_t *);
+
+/* SMB2 signing routines - smb2_signing.c */
+int smb2_sign_check_request(smb_request_t *);
+void smb2_sign_reply(smb_request_t *);
+
+uint32_t smb2_fsctl_vneginfo(smb_request_t *, smb_fsctl_t *);
+
+smb_sdrc_t smb2_negotiate(smb_request_t *);
+smb_sdrc_t smb2_session_setup(smb_request_t *);
+smb_sdrc_t smb2_logoff(smb_request_t *);
+smb_sdrc_t smb2_tree_connect(smb_request_t *);
+smb_sdrc_t smb2_tree_disconn(smb_request_t *);
+smb_sdrc_t smb2_create(smb_request_t *);
+smb_sdrc_t smb2_close(smb_request_t *);
+smb_sdrc_t smb2_flush(smb_request_t *);
+smb_sdrc_t smb2_read(smb_request_t *);
+smb_sdrc_t smb2_write(smb_request_t *);
+smb_sdrc_t smb2_lock(smb_request_t *);
+smb_sdrc_t smb2_ioctl(smb_request_t *);
+/* No smb2_cancel() - see smb2_dispatch.c */
+smb_sdrc_t smb2_echo(smb_request_t *);
+smb_sdrc_t smb2_query_dir(smb_request_t *);
+smb_sdrc_t smb2_change_notify(smb_request_t *);
+smb_sdrc_t smb2_query_info(smb_request_t *);
+smb_sdrc_t smb2_set_info(smb_request_t *);
+smb_sdrc_t smb2_oplock_break_ack(smb_request_t *);
+
+int smb2_newrq_negotiate(smb_request_t *);
+
+uint32_t smb2_ofile_getattr(smb_request_t *, smb_ofile_t *, smb_attr_t *);
+uint32_t smb2_ofile_getstd(smb_ofile_t *, smb_queryinfo_t *);
+uint32_t smb2_ofile_getname(smb_ofile_t *, smb_queryinfo_t *);
+
+uint32_t smb2_qinfo_file(smb_request_t *, smb_queryinfo_t *);
+uint32_t smb2_qinfo_fs(smb_request_t *, smb_queryinfo_t *);
+uint32_t smb2_qinfo_sec(smb_request_t *, smb_queryinfo_t *);
+uint32_t smb2_qinfo_quota(smb_request_t *, smb_queryinfo_t *);
+uint32_t smb2_qinfo_stream(smb_request_t *, smb_queryinfo_t *);
+
+uint32_t smb2_setinfo_file(smb_request_t *, smb_setinfo_t *, int);
+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 *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMB2_KPROTO_H_ */
diff --git a/usr/src/uts/common/smbsrv/smb_dfs.h b/usr/src/uts/common/smbsrv/smb_dfs.h
index 1e10dce989..25e80838c7 100644
--- a/usr/src/uts/common/smbsrv/smb_dfs.h
+++ b/usr/src/uts/common/smbsrv/smb_dfs.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SMB_DFS_H
@@ -99,6 +100,9 @@ extern "C" {
#define DFS_MOVE_FLAG_REPLACE_IF_EXISTS 1
+/*
+ * See also: dfs_target_pclass_xdr()
+ */
typedef enum {
DfsInvalidPriorityClass = -1,
DfsSiteCostNormalPriorityClass = 0,
@@ -130,6 +134,7 @@ typedef enum {
/*
* Referral Request Types
+ * See also: dfs_reftype_xdr()
*/
typedef enum {
DFS_REFERRAL_INVALID = 0,
@@ -140,6 +145,9 @@ typedef enum {
DFS_REFERRAL_LINK
} dfs_reftype_t;
+/*
+ * See also: dfs_target_priority_xdr()
+ */
typedef struct dfs_target_priority {
dfs_target_pclass_t p_class;
uint16_t p_rank;
@@ -158,6 +166,8 @@ typedef struct dfs_target_priority {
* lmdfs.h)
*
* t_priority priority class and rank
+ *
+ * See also: dfs_target_xdr()
*/
typedef struct dfs_target {
char t_server[DFS_SRVNAME_MAX];
@@ -166,6 +176,10 @@ typedef struct dfs_target {
dfs_target_priority_t t_priority;
} dfs_target_t;
+/*
+ * DFS referral response
+ * See also: dfs_info_xdr()
+ */
typedef struct dfs_info {
char i_uncpath[DFS_PATH_MAX];
char i_comment[DFS_COMMENT_MAX];
diff --git a/usr/src/uts/common/smbsrv/smb_door.h b/usr/src/uts/common/smbsrv/smb_door.h
index 9e284f5d9d..31d32794eb 100644
--- a/usr/src/uts/common/smbsrv/smb_door.h
+++ b/usr/src/uts/common/smbsrv/smb_door.h
@@ -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 2014 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SMBSRV_SMB_DOOR_H
@@ -48,6 +48,8 @@ extern "C" {
*
* SMB_DR_ASYNC_RESPONSE delivers the response part of an asynchronous
* request and must be processed as a synchronous request.
+ *
+ * See also: smb_doorhdr_opname()
*/
typedef enum smb_dopcode {
SMB_DR_NULL = 0,
diff --git a/usr/src/uts/common/smbsrv/smb_fsops.h b/usr/src/uts/common/smbsrv/smb_fsops.h
index cbe6111e55..f4421fe733 100644
--- a/usr/src/uts/common/smbsrv/smb_fsops.h
+++ b/usr/src/uts/common/smbsrv/smb_fsops.h
@@ -21,6 +21,8 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SMBSRV_SMB_FSOPS_H
@@ -68,10 +70,11 @@ int smb_fsop_link(smb_request_t *, cred_t *, smb_node_t *, smb_node_t *,
char *);
int smb_fsop_rename(smb_request_t *, cred_t *,
- smb_node_t *, char *, smb_node_t *,
- char *);
+ smb_node_t *, char *, smb_node_t *, char *);
int smb_fsop_setattr(smb_request_t *, cred_t *, smb_node_t *, smb_attr_t *);
+int smb_fsop_set_data_length(smb_request_t *sr, cred_t *cr, smb_node_t *,
+ offset_t);
int smb_fsop_read(smb_request_t *, cred_t *, smb_node_t *, uio_t *);
@@ -80,7 +83,7 @@ int smb_fsop_write(smb_request_t *, cred_t *, smb_node_t *, uio_t *,
int smb_fsop_statfs(cred_t *, smb_node_t *, struct statvfs64 *);
-int smb_fsop_remove_streams(smb_request_t *, cred_t *, smb_node_t *);
+uint32_t smb_fsop_remove_streams(smb_request_t *, cred_t *, smb_node_t *);
int smb_fsop_access(smb_request_t *, cred_t *, smb_node_t *, uint32_t);
diff --git a/usr/src/uts/common/smbsrv/smb_inet.h b/usr/src/uts/common/smbsrv/smb_inet.h
index 59e9e13fab..cc4f85b4a6 100644
--- a/usr/src/uts/common/smbsrv/smb_inet.h
+++ b/usr/src/uts/common/smbsrv/smb_inet.h
@@ -22,7 +22,7 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -40,6 +40,10 @@ extern "C" {
#include <sys/socket.h>
#include <netinet/in.h>
+/*
+ * SMB (internal) representation of an IP address.
+ * See also: smb_inaddr_xdr()
+ */
typedef struct smb_inaddr {
union {
in_addr_t au_ipv4;
diff --git a/usr/src/uts/common/smbsrv/smb_ioctl.h b/usr/src/uts/common/smbsrv/smb_ioctl.h
index 8ccecd7415..29cd803c25 100644
--- a/usr/src/uts/common/smbsrv/smb_ioctl.h
+++ b/usr/src/uts/common/smbsrv/smb_ioctl.h
@@ -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 2015 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SMB_IOCTL_H_
@@ -166,9 +166,12 @@ typedef struct smb_ioc_cfg {
int32_t ipv6_enable;
int32_t print_enable;
int32_t traverse_mounts;
+ uint32_t max_protocol;
uint32_t exec_flags;
uint32_t negtok_len;
smb_version_t version;
+ uint16_t initial_credits;
+ uint16_t maximum_credits;
/* SMB negotiate protocol response. */
uuid_t machine_uuid;
uchar_t negtok[SMB_PI_MAX_NEGTOK];
diff --git a/usr/src/uts/common/smbsrv/smb_kproto.h b/usr/src/uts/common/smbsrv/smb_kproto.h
index 9f8e445481..f394773c0a 100644
--- a/usr/src/uts/common/smbsrv/smb_kproto.h
+++ b/usr/src/uts/common/smbsrv/smb_kproto.h
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2007, 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.
*/
/*
@@ -56,20 +56,48 @@ extern "C" {
/*
* DTrace SDT probes have different signatures in userland than they do in
- * kernel. If they're being used in kernel code, re-define them out of
- * existence for their counterparts in libfksmbsrv
+ * kernel. If we're compiling for user mode (libfksmbsrv) define them as
+ * either no-op (for the SMB dtrace provider) or libfksmbsrv functions for
+ * the other SDT probe sites.
*/
#ifndef _KERNEL
+
+extern void smb_dtrace1(const char *f, const char *n,
+ const char *t1, long v1);
+extern void smb_dtrace2(const char *f, const char *n,
+ const char *t1, long v1,
+ const char *t2, long v2);
+extern void smb_dtrace3(const char *f, const char *n,
+ const char *t1, long v1,
+ const char *t2, long v2,
+ const char *t3, long v3);
+
+/*
+ * These are for the SMB dtrace proivder, which for a user-mode build
+ * are largely redundant with the fbt probes so make these no-ops.
+ */
#undef DTRACE_SMB_1
-#define DTRACE_SMB_1(a, b, c) ((void)c)
+#define DTRACE_SMB_1(n, a, b) ((void)b)
#undef DTRACE_SMB_2
-#define DTRACE_SMB_2(a, b, c, d, e) ((void)c, (void)e)
+#define DTRACE_SMB_2(n, a, b, c, d) ((void)b, (void)d)
+
+/*
+ * These are for the other (specialized) dtrace SDT probes sprinkled
+ * through the smbsrv code. In libfksmbsrv map these to functions.
+ */
+
#undef DTRACE_PROBE1
-#define DTRACE_PROBE1(a, b, c) ((void)c)
+#define DTRACE_PROBE1(n, a, b) \
+ smb_dtrace1(__func__, #n, #a, (long)b)
+
#undef DTRACE_PROBE2
-#define DTRACE_PROBE2(a, b, c, d, e) ((void)c, (void)e)
+#define DTRACE_PROBE2(n, a, b, c, d) \
+ smb_dtrace2(__func__, #n, #a, (long)b, #c, (long)d)
+
#undef DTRACE_PROBE3
-#define DTRACE_PROBE3(a, b, c, d, e, f, g) ((void)c, (void)e, (void)g)
+#define DTRACE_PROBE3(n, a, b, c, d, e, f) \
+ smb_dtrace3(__func__, #n, #a, (long)b, #c, (long)d, #e, (long)f)
+
#endif /* _KERNEL */
extern int smb_maxbufsize;
@@ -109,14 +137,7 @@ extern kmem_cache_t *smb_cache_event;
extern kmem_cache_t *smb_kshare_cache_vfs;
-int fd_dealloc(int);
-
-off_t lseek(int fildes, off_t offset, int whence);
-
-int arpioctl(int cmd, void *data);
-int microtime(timestruc_t *tvp);
-int clock_get_uptime(void);
-
+time_t smb_get_boottime(void);
int smb_server_lookup(smb_server_t **);
void smb_server_release(smb_server_t *);
@@ -234,9 +255,44 @@ smb_sdrc_t smb_com_trans2_query_file_information(smb_request_t *, smb_xa_t *);
smb_sdrc_t smb_com_trans2_set_path_information(smb_request_t *, smb_xa_t *);
smb_sdrc_t smb_com_trans2_set_file_information(smb_request_t *, smb_xa_t *);
smb_sdrc_t smb_com_trans2_get_dfs_referral(smb_request_t *, smb_xa_t *);
-int smb_trans2_rename(smb_request_t *, smb_node_t *, char *, int);
+uint32_t smb_query_stream_info(smb_request_t *, mbuf_chain_t *,
+ smb_queryinfo_t *);
+int smb_fssize(smb_request_t *, smb_fssize_t *);
+
+/* smb_quota.c */
uint32_t smb_quota_query_user_quota(smb_request_t *, uid_t, smb_quota_t *);
+int smb_quota_query(smb_server_t *, smb_quota_query_t *,
+ smb_quota_response_t *);
+int smb_quota_set(smb_server_t *, smb_quota_set_t *, uint32_t *);
+uint32_t smb_quota_init_sids(mbuf_chain_t *, smb_quota_query_t *,
+ smb_ofile_t *);
+uint32_t smb_quota_decode_sids(mbuf_chain_t *, list_t *);
+void smb_quota_free_sids(smb_quota_query_t *);
+void smb_quota_max_quota(mbuf_chain_t *, smb_quota_query_t *);
+uint32_t smb_quota_decode_quotas(mbuf_chain_t *, list_t *);
+uint32_t smb_quota_encode_quotas(mbuf_chain_t *, smb_quota_query_t *,
+ smb_quota_response_t *, smb_ofile_t *);
+void smb_quota_free_quotas(list_t *);
+
+void smb_query_shortname(smb_node_t *, smb_queryinfo_t *);
+
+uint32_t smb_dfs_get_referrals(smb_request_t *, smb_fsctl_t *);
+
+int smb1_newrq_negotiate(smb_request_t *);
+smb_sdrc_t smb1_negotiate_smb2(smb_request_t *sr);
+
+uint32_t smb_set_basic_info(smb_request_t *, smb_setinfo_t *);
+uint32_t smb_set_eof_info(smb_request_t *, smb_setinfo_t *);
+uint32_t smb_set_alloc_info(smb_request_t *, smb_setinfo_t *);
+uint32_t smb_set_disposition_info(smb_request_t *, smb_setinfo_t *);
+
+uint32_t smb_setinfo_rename(smb_request_t *, smb_node_t *, char *, int);
+uint32_t smb_common_rename(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
+
+uint32_t smb_setinfo_link(smb_request_t *, smb_node_t *, char *, int);
+uint32_t smb_make_link(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
+
/*
* Logging functions
@@ -250,8 +306,6 @@ int smb_net_id(uint32_t);
/*
* oplock functions - node operations
*/
-int smb_oplock_init(void);
-void smb_oplock_fini(void);
void smb_oplock_acquire(smb_request_t *sr, smb_node_t *, smb_ofile_t *);
void smb_oplock_release(smb_node_t *, smb_ofile_t *);
int smb_oplock_break(smb_request_t *, smb_node_t *, uint32_t);
@@ -259,6 +313,9 @@ void smb_oplock_break_levelII(smb_node_t *);
void smb_oplock_ack(smb_node_t *, smb_ofile_t *, uint8_t);
void smb_oplock_broadcast(smb_node_t *);
+void smb1_oplock_break_notification(smb_request_t *, uint8_t);
+void smb2_oplock_break_notification(smb_request_t *, uint8_t);
+
/*
* range lock functions - node operations
*/
@@ -308,23 +365,30 @@ uint32_t smb_omode_to_amask(uint32_t desired_access);
void sshow_distribution_info(char *);
+uint32_t smb2sr_go_async(smb_request_t *sr,
+ smb_sdrc_t (*async_func)(smb_request_t *));
+
void smb_dispatch_stats_init(smb_server_t *);
void smb_dispatch_stats_fini(smb_server_t *);
void smb_dispatch_stats_update(smb_server_t *,
smb_kstat_req_t *, int, int);
-boolean_t smb_dispatch_request(smb_request_t *);
+int smb1sr_newrq(smb_request_t *);
+void smb1sr_work(smb_request_t *);
+
int smbsr_encode_empty_result(smb_request_t *);
void smbsr_lookup_file(smb_request_t *);
void smbsr_release_file(smb_request_t *);
-int smbsr_decode_vwv(smb_request_t *sr, char *fmt, ...);
-int smbsr_decode_data(smb_request_t *sr, char *fmt, ...);
+int smbsr_decode_vwv(smb_request_t *sr, const char *, ...);
+int smbsr_decode_data(smb_request_t *sr, const char *, ...);
boolean_t smbsr_decode_data_avail(smb_request_t *);
-int smbsr_encode_result(smb_request_t *, int, int, char *, ...);
+int smbsr_encode_result(smb_request_t *, int, int, const char *, ...);
smb_xa_t *smbsr_lookup_xa(smb_request_t *sr);
void smbsr_send_reply(smb_request_t *);
+uint32_t smb_errno2status(int);
+uint16_t smb_status2doserr(uint32_t);
void smbsr_map_errno(int, smb_error_t *);
void smbsr_set_error(smb_request_t *, smb_error_t *);
void smbsr_errno(smb_request_t *, int);
@@ -333,27 +397,29 @@ void smbsr_status(smb_request_t *, DWORD, uint16_t, uint16_t);
smbsr_status(SR, ST, CL, CO)
#define smbsr_warn(SR, ST, CL, CO) \
smbsr_status(SR, ST, CL, CO)
+void smbsr_status_smb2(smb_request_t *, DWORD);
int clock_get_milli_uptime(void);
-int smb_mbc_vencodef(mbuf_chain_t *, char *, va_list);
-int smb_mbc_vdecodef(mbuf_chain_t *, char *, va_list);
-int smb_mbc_decodef(mbuf_chain_t *, char *, ...);
-int smb_mbc_encodef(mbuf_chain_t *, char *, ...);
-int smb_mbc_peek(mbuf_chain_t *, int, char *, ...);
-int smb_mbc_poke(mbuf_chain_t *, int, char *, ...);
+int smb_mbc_vencodef(mbuf_chain_t *, const char *, va_list);
+int smb_mbc_vdecodef(mbuf_chain_t *, const char *, va_list);
+int smb_mbc_decodef(mbuf_chain_t *, const char *, ...);
+int smb_mbc_encodef(mbuf_chain_t *, const char *, ...);
+int smb_mbc_peek(mbuf_chain_t *, int, const char *, ...);
+int smb_mbc_poke(mbuf_chain_t *, int, const char *, ...);
int smb_mbc_put_mem(mbuf_chain_t *, void *, int);
int smb_mbc_copy(mbuf_chain_t *, const mbuf_chain_t *, int, int);
void smbsr_encode_header(smb_request_t *sr, int wct,
- int bcc, char *fmt, ...);
+ int bcc, const char *fmt, ...);
int smb_lock_range_access(smb_request_t *, smb_node_t *,
uint64_t, uint64_t, boolean_t);
-void smb_encode_sid(smb_xa_t *, smb_sid_t *);
-smb_sid_t *smb_decode_sid(smb_xa_t *, uint32_t);
-uint32_t smb_decode_sd(smb_xa_t *, smb_sd_t *);
+void smb_encode_sd(mbuf_chain_t *, smb_sd_t *, uint32_t);
+void smb_encode_sid(mbuf_chain_t *, smb_sid_t *);
+smb_sid_t *smb_decode_sid(mbuf_chain_t *, uint32_t);
+uint32_t smb_decode_sd(mbuf_chain_t *, smb_sd_t *);
uint32_t smb_pad_align(uint32_t, uint32_t);
@@ -364,13 +430,9 @@ ksocket_t smb_socreate(int domain, int type, int protocol);
void smb_soshutdown(ksocket_t so);
void smb_sodestroy(ksocket_t so);
int smb_sorecv(ksocket_t so, void *msg, size_t len);
-void smb_net_init(void);
-void smb_net_fini(void);
void smb_net_txl_constructor(smb_txlst_t *);
void smb_net_txl_destructor(smb_txlst_t *);
-smb_txreq_t *smb_net_txr_alloc(void);
-void smb_net_txr_free(smb_txreq_t *);
-int smb_net_txr_send(ksocket_t, smb_txlst_t *, smb_txreq_t *);
+int smb_net_send_uio(smb_session_t *, struct uio *);
/*
* SMB RPC interface
@@ -380,14 +442,9 @@ int smb_opipe_open(smb_request_t *, uint32_t);
void smb_opipe_close(smb_ofile_t *);
int smb_opipe_read(smb_request_t *, struct uio *);
int smb_opipe_write(smb_request_t *, struct uio *);
-int smb_opipe_get_nread(smb_request_t *, int *);
-
-void smb_opipe_door_init(smb_server_t *);
-void smb_opipe_door_fini(smb_server_t *);
-int smb_opipe_door_open(smb_server_t *, int);
-void smb_opipe_door_close(smb_server_t *);
-int smb_opipe_door_call(smb_opipe_t *);
-void fksmb_opipe_door_open(smb_server_t *, void *);
+int smb_opipe_getattr(smb_ofile_t *, smb_attr_t *);
+int smb_opipe_getname(smb_ofile_t *, char *, size_t);
+uint32_t smb_opipe_fsctl(smb_request_t *, smb_fsctl_t *);
void smb_kdoor_init(smb_server_t *);
void smb_kdoor_fini(smb_server_t *);
@@ -400,8 +457,9 @@ void fksmb_kdoor_open(smb_server_t *, void *);
/*
* SMB server functions (file smb_server.c)
*/
+int smb_server_get_count(void);
int smb_server_g_init(void);
-int smb_server_g_fini(void);
+void smb_server_g_fini(void);
int smb_server_create(void);
int smb_server_delete(void);
int smb_server_configure(smb_ioc_cfg_t *);
@@ -524,8 +582,9 @@ void smb_vfs_rele_all(smb_export_t *);
/* NOTIFY CHANGE */
+uint32_t smb_notify_common(smb_request_t *, mbuf_chain_t *, uint32_t);
void smb_notify_event(smb_node_t *, uint_t, const char *);
-void smb_notify_file_closed(smb_ofile_t *of);
+void smb_notify_file_closed(smb_ofile_t *);
int smb_fem_fcn_install(smb_node_t *);
void smb_fem_fcn_uninstall(smb_node_t *);
@@ -546,6 +605,8 @@ int smb_sign_begin(smb_request_t *, smb_token_t *);
int smb_sign_check_request(smb_request_t *);
int smb_sign_check_secondary(smb_request_t *, unsigned int);
void smb_sign_reply(smb_request_t *, mbuf_chain_t *);
+/* SMB2, but here because it's called from common code. */
+int smb2_sign_begin(smb_request_t *, smb_token_t *);
boolean_t smb_sattr_check(uint16_t, uint16_t);
@@ -582,14 +643,14 @@ smb_tree_t *smb_session_lookup_share(smb_session_t *, const char *,
smb_tree_t *);
smb_tree_t *smb_session_lookup_volume(smb_session_t *, const char *,
smb_tree_t *);
-void smb_session_close_pid(smb_session_t *, uint16_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_session_t *, uint16_t, uint16_t, uint8_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 *);
@@ -640,8 +701,12 @@ void smb_ofile_get_quota_resume(smb_ofile_t *, char *, int);
/*
* odir functions (file smb_odir.c)
*/
-uint16_t smb_odir_open(smb_request_t *, char *, uint16_t, uint32_t);
-uint16_t smb_odir_openat(smb_request_t *, smb_node_t *);
+uint32_t smb_odir_openpath(smb_request_t *, char *, uint16_t, uint32_t,
+ smb_odir_t **);
+uint32_t smb_odir_openfh(smb_request_t *, const char *, uint16_t,
+ smb_odir_t **);
+uint32_t smb_odir_openat(smb_request_t *, smb_node_t *, smb_odir_t **);
+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 *);
@@ -686,16 +751,17 @@ void smb_user_setcred(smb_user_t *, cred_t *, uint32_t);
/*
* SMB tree functions (file smb_tree.c)
*/
-smb_tree_t *smb_tree_connect(smb_request_t *);
+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 *, uint16_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 *);
int smb_tree_fclose(smb_tree_t *, uint32_t);
boolean_t smb_tree_hold(smb_tree_t *);
+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 *);
@@ -711,7 +777,7 @@ void smb_xa_rele(smb_session_t *session, smb_xa_t *xa);
int smb_xa_open(smb_xa_t *xa);
void smb_xa_close(smb_xa_t *xa);
int smb_xa_complete(smb_xa_t *xa);
-smb_xa_t *smb_xa_find(smb_session_t *session, uint16_t pid, uint16_t mid);
+smb_xa_t *smb_xa_find(smb_session_t *session, uint32_t pid, uint16_t mid);
struct mbuf *smb_mbuf_get(uchar_t *buf, int nbytes);
struct mbuf *smb_mbuf_allocate(struct uio *uio);
@@ -740,11 +806,6 @@ int smb_idpool_alloc(smb_idpool_t *pool, uint16_t *id);
void smb_idpool_free(smb_idpool_t *pool, uint16_t id);
/*
- * SMB thread function prototypes
- */
-void smb_session_worker(void *arg);
-
-/*
* SMB locked list function prototypes
*/
void smb_llist_init(void);
@@ -819,7 +880,7 @@ boolean_t smb_ace_is_generic(int);
boolean_t smb_ace_is_access(int);
boolean_t smb_ace_is_audit(int);
-uint32_t smb_vss_ioctl_enumerate_snaps(smb_request_t *, smb_xa_t *);
+uint32_t smb_vss_enum_snapshots(smb_request_t *, smb_fsctl_t *);
int smb_vss_lookup_nodes(smb_request_t *, smb_node_t *, smb_node_t *,
char *, smb_node_t **, smb_node_t **);
vnode_t *smb_lookuppathvptovp(smb_request_t *, char *, vnode_t *, vnode_t *);
diff --git a/usr/src/uts/common/smbsrv/smb_kstat.h b/usr/src/uts/common/smbsrv/smb_kstat.h
index 0b3190dc12..c3816f563a 100644
--- a/usr/src/uts/common/smbsrv/smb_kstat.h
+++ b/usr/src/uts/common/smbsrv/smb_kstat.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -29,6 +30,7 @@
#define _SMBSRV_SMB_KSTAT_H
#include <smbsrv/smb.h>
+#include <smbsrv/smb2.h>
#ifdef __cplusplus
extern "C" {
@@ -80,7 +82,8 @@ typedef struct smbsrv_kstats {
uint64_t ks_rxb; /* Bytes received */
uint64_t ks_nreq; /* Requests treated */
smb_kstat_utilization_t ks_utilization;
- smb_kstat_req_t ks_reqs[SMB_COM_NUM];
+ smb_kstat_req_t ks_reqs1[SMB_COM_NUM];
+ smb_kstat_req_t ks_reqs2[SMB2__NCMDS];
uint32_t ks_nbt_sess; /* NBT sessions */
uint32_t ks_tcp_sess; /* TCP sessions */
uint32_t ks_users; /* Users logged in */
diff --git a/usr/src/uts/common/smbsrv/smb_ktypes.h b/usr/src/uts/common/smbsrv/smb_ktypes.h
index 42428b2b13..c6a22a6406 100644
--- a/usr/src/uts/common/smbsrv/smb_ktypes.h
+++ b/usr/src/uts/common/smbsrv/smb_ktypes.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -50,6 +50,7 @@ extern "C" {
#include <sys/ksocket.h>
#include <sys/fem.h>
#include <smbsrv/smb.h>
+#include <smbsrv/smb2.h>
#include <smbsrv/smbinfo.h>
#include <smbsrv/mbuf.h>
#include <smbsrv/smb_sid.h>
@@ -263,7 +264,7 @@ typedef void (*smb_thread_ep_t)(struct _smb_thread *, void *ep_arg);
typedef struct _smb_thread {
uint32_t sth_magic;
- char sth_name[16];
+ char sth_name[32];
smb_thread_state_t sth_state;
kthread_t *sth_th;
kt_did_t sth_did;
@@ -327,8 +328,8 @@ typedef struct smb_idpool {
#define SMB_TXREQ_MAGIC 0X54524251 /* 'TREQ' */
typedef struct {
- uint32_t tr_magic;
list_node_t tr_lnd;
+ uint32_t tr_magic;
int tr_len;
uint8_t tr_buf[SMB_XPRT_MAX_SIZE];
} smb_txreq_t;
@@ -337,8 +338,8 @@ typedef struct {
typedef struct {
uint32_t tl_magic;
kmutex_t tl_mutex;
+ kcondvar_t tl_wait_cv;
boolean_t tl_active;
- list_t tl_list;
} smb_txlst_t;
/*
@@ -373,8 +374,8 @@ typedef struct {
typedef void (*smb_dtorproc_t)(void *);
typedef struct smb_dtor {
- uint32_t dt_magic;
list_node_t dt_lnd;
+ uint32_t dt_magic;
void *dt_object;
smb_dtorproc_t dt_proc;
} smb_dtor_t;
@@ -567,6 +568,7 @@ typedef struct smb_oplock {
kcondvar_t ol_cv;
kthread_t *ol_xthread;
boolean_t ol_fem; /* fem monitor installed? */
+ uint8_t ol_brk_pending;
uint8_t ol_break;
uint32_t ol_count; /* number of grants */
list_t ol_grants; /* list of smb_oplock_grant_t */
@@ -578,8 +580,9 @@ typedef struct smb_oplock {
#define SMB_OFILE_OPLOCK_GRANTED(p) \
((p)->f_oplock_grant.og_magic == SMB_OPLOCK_GRANT_MAGIC)
typedef struct smb_oplock_grant {
- uint32_t og_magic;
list_node_t og_lnd;
+ uint32_t og_magic;
+ uint8_t og_breaking;
uint8_t og_level;
uint16_t og_fid;
uint16_t og_tid;
@@ -592,8 +595,8 @@ typedef struct smb_oplock_grant {
#define SMB_OPLOCK_BREAK_VALID(p) \
ASSERT((p)->ob_magic == SMB_OPLOCK_BREAK_MAGIC)
typedef struct smb_oplock_break {
- uint32_t ob_magic;
list_node_t ob_lnd;
+ uint32_t ob_magic;
struct smb_node *ob_node;
} smb_oplock_break_t;
@@ -601,8 +604,8 @@ typedef struct smb_oplock_break {
#define SMB_VFS_MAGIC 0x534D4256 /* 'SMBV' */
typedef struct smb_vfs {
- uint32_t sv_magic;
list_node_t sv_lnd;
+ uint32_t sv_magic;
uint32_t sv_refcnt;
vfs_t *sv_vfsp;
vnode_t *sv_rootvp;
@@ -625,10 +628,10 @@ typedef enum {
* delete_on_close_cred credentials for delayed delete
*/
typedef struct smb_node {
+ list_node_t n_lnd;
uint32_t n_magic;
krwlock_t n_lock;
kmutex_t n_mutex;
- list_node_t n_lnd;
smb_node_state_t n_state;
uint32_t n_refcnt;
uint32_t n_hashkey;
@@ -657,6 +660,7 @@ typedef struct smb_node {
#define NODE_FLAGS_SYSTEM 0x00008000
#define NODE_FLAGS_WRITE_THROUGH 0x00100000
#define NODE_XATTR_DIR 0x01000000
+#define NODE_FLAGS_WATCH_TREE 0x10000000 /* smb_notify.c */
#define NODE_FLAGS_DELETE_ON_CLOSE 0x40000000
#define NODE_FLAGS_EXECUTABLE 0x80000000
@@ -729,10 +733,10 @@ typedef struct smb_arg_sessionsetup {
uint32_t ssi_capabilities;
int ssi_native_os;
int ssi_native_lm;
- boolean_t ssi_guest;
} smb_arg_sessionsetup_t;
typedef struct tcon {
+ char *name;
char *path;
char *service;
int pwdlen;
@@ -764,22 +768,7 @@ typedef struct tcon {
#define SMB_SESSION_OFILE_MAX (16 * 1024)
-/*
- * When a connection is set up we need to remember both the client
- * (peer) IP address and the local IP address used to establish the
- * connection. When a client connects with a vc number of zero, we
- * are supposed to abort any existing connections with that client
- * (see notes in smb_negotiate.c and smb_session_setup_andx.c). For
- * servers with multiple network interfaces or IP aliases, however,
- * each interface has to be managed independently since the client
- * is not aware of the server configuration. We have to allow the
- * client to establish a connection on each interface with a vc
- * number of zero without aborting the other connections.
- *
- * ipaddr: the client (peer) IP address for the session.
- * local_ipaddr: the local IP address used to connect to the server.
- */
-
+/* SMB1 signing */
struct smb_sign {
unsigned int flags;
uint32_t seqnum;
@@ -787,6 +776,14 @@ struct smb_sign {
uint8_t *mackey;
};
+/*
+ * SMB2 signing
+ */
+struct smb_key {
+ uint_t len;
+ uint8_t key[SMB2_SESSION_KEY_LEN];
+};
+
#define SMB_SIGNING_ENABLED 1
#define SMB_SIGNING_CHECK 2
@@ -798,35 +795,18 @@ struct smb_sign {
* | SMB_SESSION_STATE_CONNECTED | | SMB_SESSION_STATE_TERMINATED |
* +-----------------------------+ +------------------------------+
* T0| ^
- * +--------------------+ |T13
- * v |T14 |
+ * +--------------------+ |T5
+ * v |T4 |
* +-------------------------------+ | +--------------------------------+
* | SMB_SESSION_STATE_ESTABLISHED |---+--->| SMB_SESSION_STATE_DISCONNECTED |
* +-------------------------------+ +--------------------------------+
- * T1| ^ ^ ^ ^
- * +----------+ |T9 | | |
- * v | | | |
- * +------------------------------+ | | |
- * | SMB_SESSION_STATE_NEGOTIATED | | | |
- * +------------------------------+ | | |
- * ^| ^| | ^ | | |
- * +----------------+| || | | | | |
- * |+----------------+ || T7| |T8 | | |
- * || || | | | | |
- * || +----------------+| | | | | |
- * || |+----------------+ | | | | |
- * || || v | | | |
- * || || +-----------------------------------+ T10| | |
- * || || | SMB_SESSION_STATE_OPLOCK_BREAKING |----+ | |
- * || || +-----------------------------------+ | |
- * || ||T5 | |
- * || |+-->+-----------------------------------+ T11| |
- * || |T6 | SMB_SESSION_STATE_READ_RAW_ACTIVE |------+ |
- * || +----+-----------------------------------+ |
- * ||T3 |
- * |+------->+------------------------------------+ T12|
- * |T4 | SMB_SESSION_STATE_WRITE_RAW_ACTIVE |-------+
- * +---------+------------------------------------+
+ * T1| ^
+ * +----------+ |T3
+ * v |
+ * +------------------------------+
+ * | SMB_SESSION_STATE_NEGOTIATED |
+ * +------------------------------+
+ *
*
* Transition T0
*
@@ -852,42 +832,6 @@ struct smb_sign {
*
*
*
- * Transition T6
- *
- *
- *
- * Transition T7
- *
- *
- *
- * Transition T8
- *
- *
- *
- * Transition T9
- *
- *
- *
- * Transition T10
- *
- *
- *
- * Transition T11
- *
- *
- *
- * Transition T12
- *
- *
- *
- * Transition T13
- *
- *
- *
- * Transition T14
- *
- *
- *
*/
#define SMB_SESSION_MAGIC 0x53455353 /* 'SESS' */
#define SMB_SESSION_VALID(p) \
@@ -901,33 +845,38 @@ typedef enum {
SMB_SESSION_STATE_CONNECTED,
SMB_SESSION_STATE_ESTABLISHED,
SMB_SESSION_STATE_NEGOTIATED,
- SMB_SESSION_STATE_OPLOCK_BREAKING,
SMB_SESSION_STATE_TERMINATED,
SMB_SESSION_STATE_SENTINEL
} smb_session_state_t;
typedef struct smb_session {
+ list_node_t s_lnd;
uint32_t s_magic;
smb_rwx_t s_lock;
- list_node_t s_lnd;
uint64_t s_kid;
smb_session_state_t s_state;
uint32_t s_flags;
taskqid_t s_receiver_tqid;
kthread_t *s_thread;
kt_did_t s_ktdid;
- smb_kmod_cfg_t s_cfg;
+ int (*newrq_func)(struct smb_request *);
struct smb_server *s_server;
+ smb_kmod_cfg_t s_cfg;
int32_t s_gmtoff;
uint32_t keep_alive;
uint64_t opentime;
uint16_t s_local_port;
+ uint16_t s_remote_port;
smb_inaddr_t ipaddr;
smb_inaddr_t local_ipaddr;
int dialect;
int native_os;
int native_lm;
+ kmutex_t s_credits_mutex;
+ uint16_t s_cur_credits;
+ uint16_t s_max_credits;
+
uint32_t capabilities;
struct smb_sign signing; /* SMB1 */
@@ -952,19 +901,23 @@ typedef struct smb_session {
uint32_t sesskey;
uint32_t challenge_len;
unsigned char challenge_key[SMB_CHALLENGE_SZ];
- unsigned char MAC_key[44];
int64_t activity_timestamp;
/*
- * Maximum negotiated buffer size between SMB client and server
+ * Maximum negotiated buffer sizes between SMB client and server
* in SMB_SESSION_SETUP_ANDX
*/
+ int cmd_max_bytes;
+ int reply_max_bytes;
uint16_t smb_msg_size;
uint16_t smb_max_mpx;
uchar_t *outpipe_data;
int outpipe_datalen;
int outpipe_cookie;
smb_srqueue_t *s_srqueue;
+ uint64_t start_time;
+ unsigned char MAC_key[44];
char ip_addr_str[INET6_ADDRSTRLEN];
+ char clnt_uuid[16];
char workstation[SMB_PI_MAX_HOST];
} smb_session_t;
@@ -973,7 +926,7 @@ typedef struct smb_session {
ASSERT(((u) != NULL) && ((u)->u_magic == SMB_USER_MAGIC))
#define SMB_USER_FLAG_GUEST SMB_ATF_GUEST
-#define SMB_USER_FLAG_IPC SMB_ATF_ANON
+#define SMB_USER_FLAG_ANON SMB_ATF_ANON
#define SMB_USER_FLAG_ADMIN SMB_ATF_ADMIN
#define SMB_USER_FLAG_POWER_USER SMB_ATF_POWERUSER
#define SMB_USER_FLAG_BACKUP_OPERATOR SMB_ATF_BACKUPOP
@@ -996,8 +949,8 @@ typedef enum {
} smb_user_state_t;
typedef struct smb_user {
- uint32_t u_magic;
list_node_t u_lnd;
+ uint32_t u_magic;
kmutex_t u_mutex;
smb_user_state_t u_state;
@@ -1017,6 +970,9 @@ typedef struct smb_user {
uint32_t u_privileges;
uint16_t u_uid;
uint32_t u_audit_sid;
+
+ uint32_t u_sign_flags;
+ struct smb_key u_sign_key; /* SMB2 signing */
} smb_user_t;
#define SMB_TREE_MAGIC 0x54524545 /* 'TREE' */
@@ -1055,9 +1011,9 @@ typedef enum {
} smb_tree_state_t;
typedef struct smb_tree {
+ list_node_t t_lnd;
uint32_t t_magic;
kmutex_t t_mutex;
- list_node_t t_lnd;
smb_tree_state_t t_state;
struct smb_server *t_server;
@@ -1153,6 +1109,86 @@ typedef struct smb_tree {
(SMB_TREE_IS_READONLY((sr)) || \
smb_node_file_is_readonly((node)))
+#define SMB_ODIR_MAGIC 0x4F444952 /* 'ODIR' */
+#define SMB_ODIR_VALID(p) \
+ ASSERT((p != NULL) && ((p)->d_magic == SMB_ODIR_MAGIC))
+
+#define SMB_ODIR_BUFSIZE (8 * 1024)
+
+#define SMB_ODIR_FLAG_WILDCARDS 0x0001
+#define SMB_ODIR_FLAG_IGNORE_CASE 0x0002
+#define SMB_ODIR_FLAG_XATTR 0x0004
+#define SMB_ODIR_FLAG_EDIRENT 0x0008
+#define SMB_ODIR_FLAG_CATIA 0x0010
+#define SMB_ODIR_FLAG_ABE 0x0020
+#define SMB_ODIR_FLAG_SHORTNAMES 0x0040
+
+typedef enum {
+ SMB_ODIR_STATE_OPEN = 0,
+ SMB_ODIR_STATE_IN_USE,
+ SMB_ODIR_STATE_CLOSING,
+ SMB_ODIR_STATE_CLOSED,
+ SMB_ODIR_STATE_SENTINEL
+} smb_odir_state_t;
+
+typedef enum {
+ SMB_ODIR_RESUME_CONT,
+ SMB_ODIR_RESUME_IDX,
+ SMB_ODIR_RESUME_COOKIE,
+ SMB_ODIR_RESUME_FNAME
+} smb_odir_resume_type_t;
+
+typedef struct smb_odir_resume {
+ smb_odir_resume_type_t or_type;
+ int or_idx;
+ uint32_t or_cookie;
+ char *or_fname;
+} smb_odir_resume_t;
+
+/*
+ * Flags used when opening an odir
+ */
+#define SMB_ODIR_OPENF_BACKUP_INTENT 0x01
+
+typedef struct smb_odir {
+ list_node_t d_lnd;
+ uint32_t d_magic;
+ kmutex_t d_mutex;
+ smb_odir_state_t d_state;
+ smb_session_t *d_session;
+ smb_user_t *d_user;
+ smb_tree_t *d_tree;
+ smb_node_t *d_dnode;
+ cred_t *d_cred;
+ uint32_t d_opened_by_pid;
+ uint16_t d_odid;
+ uint16_t d_sattr;
+ uint32_t d_refcnt;
+ uint32_t d_flags;
+ boolean_t d_eof;
+ int d_bufsize;
+ uint64_t d_offset;
+ union {
+ char *u_bufptr;
+ struct edirent *u_edp;
+ struct dirent64 *u_dp;
+ } d_u;
+ uint32_t d_last_cookie;
+ uint32_t d_cookies[SMB_MAX_SEARCH];
+ char d_pattern[MAXNAMELEN];
+ char d_buf[SMB_ODIR_BUFSIZE];
+ char d_last_name[MAXNAMELEN];
+} smb_odir_t;
+#define d_bufptr d_u.u_bufptr
+#define d_edp d_u.u_edp
+#define d_dp d_u.u_dp
+
+typedef struct smb_odirent {
+ char od_name[MAXNAMELEN]; /* on disk name */
+ ino64_t od_ino;
+ uint32_t od_eflags;
+} smb_odirent_t;
+
#define SMB_OPIPE_MAGIC 0x50495045 /* 'PIPE' */
#define SMB_OPIPE_VALID(p) \
ASSERT(((p) != NULL) && (p)->p_magic == SMB_OPIPE_MAGIC)
@@ -1215,10 +1251,10 @@ typedef enum {
} smb_ofile_state_t;
typedef struct smb_ofile {
+ list_node_t f_lnd; /* t_ofile_list */
+ list_node_t f_nnd; /* n_ofile_list */
uint32_t f_magic;
kmutex_t f_mutex;
- list_node_t f_lnd;
- list_node_t f_nnd;
smb_ofile_state_t f_state;
struct smb_server *f_server;
@@ -1226,6 +1262,7 @@ typedef struct smb_ofile {
smb_user_t *f_user;
smb_tree_t *f_tree;
smb_node_t *f_node;
+ smb_odir_t *f_odir;
smb_opipe_t *f_pipe;
uint32_t f_uniqid;
@@ -1235,8 +1272,8 @@ typedef struct smb_ofile {
uint32_t f_granted_access;
uint32_t f_share_access;
uint32_t f_create_options;
+ uint32_t f_opened_by_pid;
uint16_t f_fid;
- uint16_t f_opened_by_pid;
uint16_t f_ftype;
uint64_t f_llf_pos;
int f_mode;
@@ -1248,86 +1285,6 @@ typedef struct smb_ofile {
smb_oplock_grant_t f_oplock_grant;
} smb_ofile_t;
-#define SMB_ODIR_MAGIC 0x4F444952 /* 'ODIR' */
-#define SMB_ODIR_VALID(p) \
- ASSERT((p != NULL) && ((p)->d_magic == SMB_ODIR_MAGIC))
-
-#define SMB_ODIR_BUFSIZE (8 * 1024)
-
-#define SMB_ODIR_FLAG_WILDCARDS 0x0001
-#define SMB_ODIR_FLAG_IGNORE_CASE 0x0002
-#define SMB_ODIR_FLAG_XATTR 0x0004
-#define SMB_ODIR_FLAG_EDIRENT 0x0008
-#define SMB_ODIR_FLAG_CATIA 0x0010
-#define SMB_ODIR_FLAG_ABE 0x0020
-#define SMB_ODIR_FLAG_SHORTNAMES 0x0040
-
-typedef enum {
- SMB_ODIR_STATE_OPEN = 0,
- SMB_ODIR_STATE_IN_USE,
- SMB_ODIR_STATE_CLOSING,
- SMB_ODIR_STATE_CLOSED,
- SMB_ODIR_STATE_SENTINEL
-} smb_odir_state_t;
-
-typedef enum {
- SMB_ODIR_RESUME_CONT,
- SMB_ODIR_RESUME_IDX,
- SMB_ODIR_RESUME_COOKIE,
- SMB_ODIR_RESUME_FNAME
-} smb_odir_resume_type_t;
-
-typedef struct smb_odir_resume {
- smb_odir_resume_type_t or_type;
- int or_idx;
- uint32_t or_cookie;
- char *or_fname;
-} smb_odir_resume_t;
-
-/*
- * Flags used when opening an odir
- */
-#define SMB_ODIR_OPENF_BACKUP_INTENT 0x01
-
-typedef struct smb_odir {
- uint32_t d_magic;
- kmutex_t d_mutex;
- list_node_t d_lnd;
- smb_odir_state_t d_state;
- smb_session_t *d_session;
- smb_user_t *d_user;
- smb_tree_t *d_tree;
- smb_node_t *d_dnode;
- cred_t *d_cred;
- uint16_t d_odid;
- uint16_t d_opened_by_pid;
- uint16_t d_sattr;
- uint32_t d_refcnt;
- uint32_t d_flags;
- boolean_t d_eof;
- int d_bufsize;
- uint64_t d_offset;
- union {
- char *u_bufptr;
- struct edirent *u_edp;
- struct dirent64 *u_dp;
- } d_u;
- uint32_t d_last_cookie;
- uint32_t d_cookies[SMB_MAX_SEARCH];
- char d_pattern[MAXNAMELEN];
- char d_buf[SMB_ODIR_BUFSIZE];
- char d_last_name[MAXNAMELEN];
-} smb_odir_t;
-#define d_bufptr d_u.u_bufptr
-#define d_edp d_u.u_edp
-#define d_dp d_u.u_dp
-
-typedef struct smb_odirent {
- char od_name[MAXNAMELEN]; /* on disk name */
- ino64_t od_ino;
- uint32_t od_eflags;
-} smb_odirent_t;
-
typedef struct smb_fileinfo {
char fi_name[MAXNAMELEN];
char fi_shortname[SMB_SHORTNAMELEN];
@@ -1351,9 +1308,9 @@ typedef struct smb_streaminfo {
#define SMB_LOCK_MAGIC 0x4C4F434B /* 'LOCK' */
typedef struct smb_lock {
+ list_node_t l_lnd;
uint32_t l_magic;
kmutex_t l_mutex;
- list_node_t l_lnd;
kcondvar_t l_cv;
list_node_t l_conflict_lnd;
@@ -1367,7 +1324,7 @@ typedef struct smb_lock {
uint64_t l_session_kid;
struct smb_lock *l_blocked_by; /* Debug info only */
- uint16_t l_pid;
+ uint32_t l_pid;
uint16_t l_uid;
uint32_t l_type;
uint64_t l_start;
@@ -1434,6 +1391,61 @@ typedef struct dirop {
uint16_t flags;
} smb_arg_dirop_t;
+typedef struct smb_queryinfo {
+ smb_node_t *qi_node; /* NULL for pipes */
+ uint8_t qi_InfoType;
+ uint8_t qi_InfoClass;
+ uint8_t qi_delete_on_close;
+ uint8_t qi_isdir;
+ uint32_t qi_AddlInfo;
+ uint32_t qi_Flags;
+ mbuf_chain_t in_data;
+ smb_attr_t qi_attr;
+ uint32_t qi_namelen;
+ char qi_shortname[SMB_SHORTNAMELEN];
+ char qi_name[MAXPATHLEN];
+} smb_queryinfo_t;
+
+typedef struct smb_setinfo {
+ smb_node_t *si_node;
+ mbuf_chain_t si_data;
+ smb_attr_t si_attr;
+} smb_setinfo_t;
+
+/*
+ * smb_fssize_t
+ * volume_units and volume avail are the total allocated and
+ * available units on the volume.
+ * caller_units and caller_avail are the allocated and available
+ * units on the volume for the user associated with the calling
+ * thread.
+ */
+typedef struct smb_fssize {
+ uint64_t fs_volume_units;
+ uint64_t fs_volume_avail;
+ uint64_t fs_caller_units;
+ uint64_t fs_caller_avail;
+ uint32_t fs_sectors_per_unit;
+ uint32_t fs_bytes_per_sector;
+} smb_fssize_t;
+
+/*
+ * SMB FsCtl operations (SMB2 Ioctl, and some SMB1 trans calls)
+ */
+typedef struct {
+ uint32_t CtlCode;
+ uint32_t InputCount;
+ uint32_t OutputCount;
+ uint32_t MaxOutputResp;
+ mbuf_chain_t *in_mbc;
+ mbuf_chain_t *out_mbc;
+} smb_fsctl_t;
+
+typedef struct {
+ uint64_t persistent;
+ uint64_t temporal;
+} smb2fid_t;
+
typedef struct {
uint32_t status;
uint16_t errcls;
@@ -1449,11 +1461,17 @@ typedef struct open_param {
uint32_t dattr;
timestruc_t crtime;
timestruc_t mtime;
- uint64_t dsize;
+ timestruc_t timewarp;
+ /*
+ * Careful: dsize is the desired (allocation) size before the
+ * common open function, and the actual size afterwards.
+ */
+ uint64_t dsize; /* alloc size, actual size */
uint32_t desired_access;
uint32_t share_access;
uint32_t create_options;
uint32_t create_disposition;
+ boolean_t create_timewarp;
boolean_t created_readonly;
uint32_t ftype;
uint32_t devstate;
@@ -1467,6 +1485,8 @@ typedef struct open_param {
boolean_t op_oplock_levelII; /* TRUE if levelII supported */
} smb_arg_open_t;
+struct smb_async_req;
+
/*
* SMB Request State Machine
* -------------------------
@@ -1603,15 +1623,16 @@ typedef enum smb_req_state {
} smb_req_state_t;
typedef struct smb_request {
+ list_node_t sr_session_lnd;
uint32_t sr_magic;
kmutex_t sr_mutex;
- list_node_t sr_session_lnd;
smb_req_state_t sr_state;
struct smb_server *sr_server;
pid_t *sr_pid;
int32_t sr_gmtoff;
smb_session_t *session;
smb_kmod_cfg_t *sr_cfg;
+
smb_notify_change_req_t sr_ncr;
/* Info from session service header */
@@ -1640,15 +1661,43 @@ typedef struct smb_request {
uint8_t smb_flg; /* flags */
uint16_t smb_flg2; /* flags */
- uint16_t smb_pid_high; /* high part of pid */
unsigned char smb_sig[8]; /* signiture */
uint16_t smb_tid; /* tree id # */
- uint16_t smb_pid; /* caller's process id # */
+ uint32_t smb_pid; /* caller's process id # */
uint16_t smb_uid; /* user id # */
uint16_t smb_mid; /* mutiplex id # */
unsigned char smb_wct; /* count of parameter words */
uint16_t smb_bcc; /* data byte count */
+ /*
+ * Beginning offsets (in the mbuf chain) for the
+ * command and reply headers, and the next reply.
+ */
+ uint32_t smb2_cmd_hdr;
+ uint32_t smb2_reply_hdr;
+ uint32_t smb2_next_reply;
+
+ /*
+ * SMB2 header fields. [MS-SMB2 2.2.1.2]
+ * XXX: Later do a union w smb1 members
+ */
+ uint16_t smb2_credit_charge;
+ uint16_t smb2_chan_seq; /* cmd only */
+ uint32_t smb2_status;
+ uint16_t smb2_cmd_code;
+ uint16_t smb2_credit_request;
+ uint16_t smb2_credit_response;
+ uint32_t smb2_hdr_flags;
+ uint32_t smb2_next_command;
+ uint64_t smb2_messageid;
+ /* 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_async_id;
+ struct smb2_async_req *sr_async_req;
+
/* Parameters */
struct mbuf_chain smb_vwv; /* variable width value */
@@ -1679,6 +1728,7 @@ typedef struct smb_request {
smb_arg_dirop_t dirop;
smb_arg_open_t open;
smb_rw_param_t *rw;
+ smb_oplock_grant_t olbrk; /* for async oplock break */
int32_t timestamp;
} arg;
} smb_request_t;
@@ -1721,9 +1771,9 @@ typedef struct smb_request {
#define SMB_XA_MAGIC 0x534D4258 /* 'SMBX' */
typedef struct smb_xa {
+ list_node_t xa_lnd;
uint32_t xa_magic;
kmutex_t xa_mutex;
- list_node_t xa_lnd;
uint32_t xa_refcnt;
uint32_t xa_flags;
@@ -1734,7 +1784,7 @@ typedef struct smb_xa {
unsigned char smb_flg; /* flags */
uint16_t smb_flg2; /* flags */
uint16_t smb_tid; /* tree id number */
- uint16_t smb_pid; /* caller's process id number */
+ uint32_t smb_pid; /* caller's process id */
uint16_t smb_uid; /* user id number */
uint32_t smb_func; /* NT_TRANS function */
@@ -1857,10 +1907,10 @@ typedef struct {
((S) == SMB_SERVER_STATE_DELETING))
typedef struct smb_server {
+ list_node_t sv_lnd;
uint32_t sv_magic;
kcondvar_t sv_cv;
kmutex_t sv_mutex;
- list_node_t sv_lnd;
smb_server_state_t sv_state;
uint32_t sv_refcnt;
pid_t sv_pid;
@@ -1911,7 +1961,8 @@ typedef struct smb_server {
smb_cmd_threshold_t sv_opipe_ct;
kstat_t *sv_legacy_ksp;
kmutex_t sv_legacy_ksmtx;
- smb_disp_stats_t *sv_disp_stats;
+ smb_disp_stats_t *sv_disp_stats1;
+ smb_disp_stats_t *sv_disp_stats2;
} smb_server_t;
#define SMB_EVENT_MAGIC 0x45564E54 /* EVNT */
@@ -1919,8 +1970,8 @@ typedef struct smb_server {
#define SMB_EVENT_VALID(e) \
ASSERT(((e) != NULL) && ((e)->se_magic == SMB_EVENT_MAGIC))
typedef struct smb_event {
- uint32_t se_magic;
list_node_t se_lnd;
+ uint32_t se_magic;
kmutex_t se_mutex;
kcondvar_t se_cv;
smb_server_t *se_server;
@@ -1932,8 +1983,8 @@ typedef struct smb_event {
} smb_event_t;
typedef struct smb_kspooldoc {
- uint32_t sd_magic;
list_node_t sd_lnd;
+ uint32_t sd_magic;
smb_inaddr_t sd_ipaddr;
uint32_t sd_spool_num;
uint16_t sd_fid;
@@ -1942,8 +1993,8 @@ typedef struct smb_kspooldoc {
} smb_kspooldoc_t;
typedef struct smb_spoolfid {
- uint32_t sf_magic;
list_node_t sf_lnd;
+ uint32_t sf_magic;
uint16_t sf_fid;
} smb_spoolfid_t;
diff --git a/usr/src/uts/common/smbsrv/smb_privilege.h b/usr/src/uts/common/smbsrv/smb_privilege.h
index 0bfd6cbdbf..1a1ee196c1 100644
--- a/usr/src/uts/common/smbsrv/smb_privilege.h
+++ b/usr/src/uts/common/smbsrv/smb_privilege.h
@@ -21,6 +21,8 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SMB_PRIVILEGE_H
@@ -135,18 +137,28 @@ extern "C" {
*/
#define PRIVILEGE_SET_ALL_NECESSARY 1
+/*
+ * Local User ID (an NT thing, not a Unix UID)
+ * See also: smb_luid_xdr()
+ */
typedef struct smb_luid {
uint32_t lo_part;
uint32_t hi_part;
} smb_luid_t;
-
+/*
+ * Local User ID and attributes (again, an NT thing)
+ * See also: smb_luid_attrs_xdr()
+ */
typedef struct smb_luid_attrs {
smb_luid_t luid;
uint32_t attrs;
} smb_luid_attrs_t;
-
+/*
+ * An (NT-style) collection of privileges.
+ * See also: smb_privset_xdr()
+ */
typedef struct smb_privset {
uint32_t priv_cnt;
uint32_t control;
diff --git a/usr/src/uts/common/smbsrv/smb_sid.h b/usr/src/uts/common/smbsrv/smb_sid.h
index 6bc2f0d6d5..5091f419a4 100644
--- a/usr/src/uts/common/smbsrv/smb_sid.h
+++ b/usr/src/uts/common/smbsrv/smb_sid.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SMB_SID_H
@@ -223,6 +224,7 @@ extern "C" {
* structure (SID_IDENTIFIER_AUTHORITY) containing a literal
* definition of a 6 byte vector but the effect is the same
* as defining it as a member value.
+ * See also: smb_sid_xdr()
*/
typedef struct smb_sid {
uint8_t sid_revision;
@@ -256,6 +258,7 @@ typedef struct smb_sid {
/*
* smb_id_t consists of both the Windows security identifier
* and its corresponding POSIX/ephemeral ID.
+ * See also: smb_id_xdr()
*/
typedef struct smb_id {
uint32_t i_attrs;
@@ -263,6 +266,10 @@ typedef struct smb_id {
uid_t i_id;
} smb_id_t;
+/*
+ * Array of smb_id_t
+ * See also: smb_ids_xdr()
+ */
typedef struct smb_ids {
uint32_t i_cnt;
smb_id_t *i_ids;
diff --git a/usr/src/uts/common/smbsrv/smb_signing.h b/usr/src/uts/common/smbsrv/smb_signing.h
index 780ceb5c1d..0d6c6bff91 100644
--- a/usr/src/uts/common/smbsrv/smb_signing.h
+++ b/usr/src/uts/common/smbsrv/smb_signing.h
@@ -16,6 +16,13 @@
#ifndef _SMB_SIGNING_H_
#define _SMB_SIGNING_H_
+/*
+ * SMB signing routines used in {smb,smb2}_signing.c
+ * Two implementations of these (kernel/user) in:
+ * uts/common/fs/smbsrv/smb_sign_kcf.c
+ * lib/smbsrv/libfksmbsrv/common/fksmb_sign_pkcs.c
+ */
+
#ifdef _KERNEL
#include <sys/crypto/api.h>
#else
@@ -28,6 +35,8 @@ extern "C" {
#endif
#define MD5_DIGEST_LENGTH 16 /* MD5 digest length in bytes */
+#define SHA256_DIGEST_LENGTH 32 /* SHA256 digest length in bytes */
+#define SMB2_SIG_SIZE 16
#ifdef _KERNEL
/* KCF variant */
@@ -40,10 +49,7 @@ typedef CK_SESSION_HANDLE smb_sign_ctx_t;
#endif /* _KERNEL */
/*
- * SMB1 signing routines used in smb_signing.c
- * Two implementations of these (kernel/user) in:
- * uts/common/fs/smbsrv/smb_sign_kcf.c
- * lib/smbsrv/libfksmbsrv/common/fksmb_sign_pkcs.c
+ * SMB signing routines used in smb_signing.c
*/
int smb_md5_getmech(smb_sign_mech_t *);
@@ -51,6 +57,15 @@ int smb_md5_init(smb_sign_ctx_t *, smb_sign_mech_t *);
int smb_md5_update(smb_sign_ctx_t, void *, size_t);
int smb_md5_final(smb_sign_ctx_t, uint8_t *);
+/*
+ * SMB2 signing routines used in smb2_signing.c
+ */
+
+int smb2_hmac_getmech(smb_sign_mech_t *);
+int smb2_hmac_init(smb_sign_ctx_t *, smb_sign_mech_t *, uint8_t *, size_t);
+int smb2_hmac_update(smb_sign_ctx_t, uint8_t *, size_t);
+int smb2_hmac_final(smb_sign_ctx_t, uint8_t *);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/smbsrv/smb_token.h b/usr/src/uts/common/smbsrv/smb_token.h
index dd20d90c9e..e30e29fa14 100644
--- a/usr/src/uts/common/smbsrv/smb_token.h
+++ b/usr/src/uts/common/smbsrv/smb_token.h
@@ -19,7 +19,6 @@
* CDDL HEADER END
*/
/*
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
@@ -82,12 +81,17 @@ typedef struct smb_buf32 {
(sizeof (smb_posix_grps_t) + (n - 1) * sizeof (gid_t))
/*
* It consists of the primary and supplementary POSIX groups.
+ * See also: smb_posix_grps_xdr()
*/
typedef struct smb_posix_grps {
uint32_t pg_ngrps;
gid_t pg_grps[ANY_SIZE_ARRAY];
} smb_posix_grps_t;
+/*
+ * An NT-style logon "token" (NT terminology)
+ * See also: smb_token_xdr()
+ */
typedef struct smb_token {
smb_id_t tkn_user;
smb_id_t tkn_owner;
@@ -104,6 +108,7 @@ typedef struct smb_token {
/*
* Details required to authenticate a user.
+ * See also: smb_logon_xdr()
*/
typedef struct smb_logon {
uint16_t lg_level;
diff --git a/usr/src/uts/common/smbsrv/smb_vops.h b/usr/src/uts/common/smbsrv/smb_vops.h
index 5262509749..2f78f63bae 100644
--- a/usr/src/uts/common/smbsrv/smb_vops.h
+++ b/usr/src/uts/common/smbsrv/smb_vops.h
@@ -129,6 +129,7 @@ int smb_vop_read(vnode_t *, uio_t *, cred_t *);
int smb_vop_write(vnode_t *, uio_t *, int, uint32_t *, cred_t *);
int smb_vop_getattr(vnode_t *, vnode_t *, smb_attr_t *, int, cred_t *);
int smb_vop_setattr(vnode_t *, vnode_t *, smb_attr_t *, int, cred_t *);
+int smb_vop_space(vnode_t *, int, flock64_t *, int, offset_t, cred_t *);
int smb_vop_access(vnode_t *, int, int, vnode_t *, cred_t *);
void smb_vop_eaccess(vnode_t *, int *, int, vnode_t *, cred_t *);
int smb_vop_lookup(vnode_t *, char *, vnode_t **, char *, int, int *, vnode_t *,
diff --git a/usr/src/uts/common/smbsrv/smb_xdr.h b/usr/src/uts/common/smbsrv/smb_xdr.h
index e4df610969..51bf3b57e2 100644
--- a/usr/src/uts/common/smbsrv/smb_xdr.h
+++ b/usr/src/uts/common/smbsrv/smb_xdr.h
@@ -53,7 +53,10 @@ extern "C" {
#include <stddef.h> /* offsetof */
#endif /* _KERNEL */
-/* null-terminated string */
+/*
+ * null-terminated string
+ * See also: smb_string_xdr()
+ */
typedef struct smb_string {
char *buf;
} smb_string_t;
@@ -103,6 +106,8 @@ typedef struct smb_pipehdr {
* resid For opipe: the number of bytes remaining in the server.
* door_rc Return code provided by the door server.
* status A pass-through status provided by the door operation.
+ *
+ * See also: smb_doorhdr_xdr()
*/
typedef struct smb_doorhdr {
uint32_t dh_magic;
@@ -116,6 +121,11 @@ typedef struct smb_doorhdr {
uint32_t dh_status;
} smb_doorhdr_t;
+/*
+ * Information about the client of a named pipe, provided by smbsrv
+ * to the server side of the named pipe (the RPC service).
+ * See also: smb_netuserinfo_xdr()
+ */
typedef struct smb_netuserinfo {
uint64_t ui_session_id;
uint16_t ui_smb_uid;
@@ -141,6 +151,10 @@ typedef struct smb_opennum {
char qualifier[MAXNAMELEN];
} smb_opennum_t;
+/*
+ * SMB (internal) representation of a tree connection (etc.)
+ * See also: smb_netconnectinfo_xdr()
+ */
typedef struct smb_netconnectinfo {
uint32_t ci_id;
uint32_t ci_type;
@@ -153,6 +167,10 @@ typedef struct smb_netconnectinfo {
char *ci_share;
} smb_netconnectinfo_t;
+/*
+ * SMB (internal) representation of an open file.
+ * See also: smb_netfileinfo_xdr()
+ */
typedef struct smb_netfileinfo {
uint16_t fi_fid;
uint32_t fi_uniqid;
@@ -220,13 +238,25 @@ bool_t lsa_account_xdr(XDR *, lsa_account_t *);
*/
#define SMB_VSS_GMT_SIZE sizeof ("@GMT-yyyy.mm.dd-hh.mm.ss")
+/*
+ * Args for enumerating "previous versions".
+ * See also: smb_gmttoken_query_xdr()
+ */
typedef struct smb_gmttoken_query {
uint32_t gtq_count;
char *gtq_path;
} smb_gmttoken_query_t;
+/*
+ * Part of response for enumerating "previous versions".
+ * See also: smb_gmttoken_xdr()
+ */
typedef char *smb_gmttoken_t;
+/*
+ * Response for enumerating "previous versions".
+ * See also: smb_gmttoken_response_xdr()
+ */
typedef struct smb_gmttoken_response {
uint32_t gtr_count;
struct {
@@ -235,9 +265,14 @@ typedef struct smb_gmttoken_response {
} gtr_gmttokens;
} smb_gmttoken_response_t;
+/*
+ * Args to lookup "previous versions" during open.
+ * See also: smb_gmttoken_snapname_xdr()
+ */
typedef struct smb_gmttoken_snapname {
char *gts_path;
char *gts_gmttoken;
+ uint64_t gts_toktime; /* seconds */
} smb_gmttoken_snapname_t;
bool_t smb_gmttoken_query_xdr(XDR *, smb_gmttoken_query_t *);
@@ -252,6 +287,10 @@ bool_t smb_gmttoken_snapname_xdr(XDR *, smb_gmttoken_snapname_t *);
*/
#define SMB_QUOTA_UNLIMITED 0xFFFFFFFFFFFFFFFF
+/*
+ * SMB (internal) representation of a quota response
+ * See also: smb_quota_xdr()
+ */
typedef struct smb_quota {
list_node_t q_list_node;
char q_sidstr[SMB_SID_STRSZ];
@@ -262,6 +301,10 @@ typedef struct smb_quota {
avl_node_t q_avl_node;
} smb_quota_t;
+/*
+ * Part of a quota response
+ * See also: smb_quota_sid_xdr()
+ */
typedef struct smb_quota_sid {
list_node_t qs_list_node;
char qs_sidstr[SMB_SID_STRSZ];
@@ -274,6 +317,10 @@ typedef enum {
SMB_QUOTA_QUERY_ALL
} smb_quota_query_op_t;
+/*
+ * SMB (internal) form of a quota lookup
+ * See also: smb_quota_query_xdr()
+ */
typedef struct smb_quota_query {
char *qq_root_path;
uint32_t qq_query_op; /* smb_quota_query_op_t */
@@ -283,11 +330,19 @@ typedef struct smb_quota_query {
list_t qq_sid_list; /* list of smb_quota_sid_t */
} smb_quota_query_t;
+/*
+ * The get quota response (list of quota records)
+ * See also: smb_quota_response_xdr()
+ */
typedef struct smb_quota_response {
uint32_t qr_status;
list_t qr_quota_list; /* list of smb_quota_t */
} smb_quota_response_t;
+/*
+ * The set quota request (list of quota records)
+ * See also: smb_quota_set_xdr()
+ */
typedef struct smb_quota_set {
char *qs_root_path;
list_t qs_quota_list; /* list of smb_quota_t */
diff --git a/usr/src/uts/common/smbsrv/smbinfo.h b/usr/src/uts/common/smbsrv/smbinfo.h
index fdb7ae8d5e..e513eb5e43 100644
--- a/usr/src/uts/common/smbsrv/smbinfo.h
+++ b/usr/src/uts/common/smbsrv/smbinfo.h
@@ -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 2015 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SMBSRV_SMBINFO_H
@@ -104,6 +104,20 @@ extern "C" {
* SMB_PI_MAX_WORKERS_MIN must therefore be < 256
*/
#define SMB_PI_MAX_WORKERS_MIN 64
+#define SMB_PI_MAX_WORKERS_DEF 1024
+#define SMB_PI_MAX_WORKERS_MAX 16384
+
+/*
+ * Min/max initial credit grant and credit limit we allow to be
+ * configured via SMB_CI_INITIAL_CREDITS, SMB_CI_MAXIMUM_CREDITS
+ */
+#define SMB_PI_INITIAL_CREDITS_MIN 16
+#define SMB_PI_INITIAL_CREDITS_DEF 20
+#define SMB_PI_INITIAL_CREDITS_MAX 256
+
+#define SMB_PI_MAXIMUM_CREDITS_MIN 64
+#define SMB_PI_MAXIMUM_CREDITS_DEF 1000
+#define SMB_PI_MAXIMUM_CREDITS_MAX 1024
/*
* sv_size is used by the RPC services and should be set to
@@ -132,10 +146,12 @@ typedef struct smb_kmod_cfg {
int32_t skc_ipv6_enable;
int32_t skc_print_enable;
int32_t skc_traverse_mounts;
+ uint32_t skc_max_protocol; /* SMB_VERS_... */
uint32_t skc_execflags;
uint32_t skc_negtok_len;
smb_version_t skc_version;
- /* SMB negotiate protocol response. */
+ uint16_t skc_initial_credits;
+ uint16_t skc_maximum_credits;
uuid_t skc_machine_uuid;
uchar_t skc_negtok[SMB_PI_MAX_NEGTOK];
char skc_native_os[SMB_PI_MAX_NATIVE_OS];
@@ -195,6 +211,16 @@ const char *smbnative_lm_str(smb_version_t *);
#define AUTH_GUEST_GRANT 0x00000001
#define AUTH_IPC_ONLY_GRANT 0x00000002
+/*
+ * Defined SMB1, SMB2(+) protocol versions, as returned by
+ * smb_config_get_max_protocol()
+ */
+#define SMB_VERS_1 1 /* arbitrary value < 0x200 */
+#define SMB_VERS_2_BASE 0x200 /* for (SMB2 or higher?) tests */
+#define SMB_VERS_2_002 0x202 /* "2.002" */
+#define SMB_VERS_2_1 0x210 /* "2.1" */
+#define SMB_VERS_3_0 0x300 /* "3.0" */
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/smbsrv/winioctl.h b/usr/src/uts/common/smbsrv/winioctl.h
index 4564b3e8a3..bbde2e4a6f 100644
--- a/usr/src/uts/common/smbsrv/winioctl.h
+++ b/usr/src/uts/common/smbsrv/winioctl.h
@@ -21,6 +21,8 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SMBSRV_WINIOCTL_H
#define _SMBSRV_WINIOCTL_H
@@ -392,25 +394,21 @@ extern "C" {
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 37, METHOD_NEITHER, FILE_ANY_ACCESS)
/* FILE_OBJECTID_BUFFER */
#define FSCTL_SET_OBJECT_ID \
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 38, METHOD_BUFFERED, \
- FILE_WRITE_ACCESS)
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 38, METHOD_BUFFERED, FILE_ANY_ACCESS)
/* FILE_OBJECTID_BUFFER */
#define FSCTL_GET_OBJECT_ID \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 39, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_DELETE_OBJECT_ID \
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 40, METHOD_BUFFERED, \
- FILE_WRITE_ACCESS)
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 40, METHOD_BUFFERED, FILE_ANY_ACCESS)
/* REPARSE_DATA_BUFFER, */
#define FSCTL_SET_REPARSE_POINT \
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, \
- FILE_WRITE_ACCESS)
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_ANY_ACCESS)
/* REPARSE_DATA_BUFFER */
#define FSCTL_GET_REPARSE_POINT \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
/* REPARSE_DATA_BUFFER, */
#define FSCTL_DELETE_REPARSE_POINT \
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 43, METHOD_BUFFERED, \
- FILE_WRITE_ACCESS)
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 43, METHOD_BUFFERED, FILE_ANY_ACCESS)
/* MFT_ENUM_DATA, */
#define FSCTL_ENUM_USN_DATA \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 44, METHOD_NEITHER, FILE_READ_ACCESS)
@@ -421,8 +419,7 @@ extern "C" {
#define FSCTL_READ_USN_JOURNAL \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 46, METHOD_NEITHER, FILE_READ_ACCESS)
#define FSCTL_SET_OBJECT_ID_EXTENDED \
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 47, METHOD_BUFFERED, \
- FILE_WRITE_ACCESS)
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 47, METHOD_BUFFERED, FILE_ANY_ACCESS)
/* FILE_OBJECTID_BUFFER */
#define FSCTL_CREATE_OR_GET_OBJECT_ID \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 48, METHOD_BUFFERED, FILE_ANY_ACCESS)
@@ -430,17 +427,15 @@ extern "C" {
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_ANY_ACCESS)
/* FILE_ZERO_DATA_INFORMATION, */
#define FSCTL_SET_ZERO_DATA \
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 50, METHOD_BUFFERED, \
- FILE_WRITE_ACCESS)
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 50, METHOD_BUFFERED, FILE_WRITE_ACCESS)
/* FILE_ALLOCATED_RANGE_BUFFER, FILE_ALLOCATED_RANGE_BUFFER */
#define FSCTL_QUERY_ALLOCATED_RANGES \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 51, METHOD_NEITHER, FILE_READ_ACCESS)
#define FSCTL_ENABLE_UPGRADE \
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 52, METHOD_BUFFERED, \
- FILE_WRITE_ACCESS)
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 52, METHOD_BUFFERED, FILE_WRITE_ACCESS)
/* ENCRYPTION_BUFFER, */
#define FSCTL_SET_ENCRYPTION \
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 53, METHOD_BUFFERED, FILE_ANY_ACCESS)
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 53, METHOD_NEITHER, FILE_ANY_ACCESS)
#define FSCTL_ENCRYPTION_FSCTL_IO \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 54, METHOD_NEITHER, FILE_ANY_ACCESS)
/* ENCRYPTED_DATA_INFO, */
@@ -454,10 +449,10 @@ extern "C" {
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 57, METHOD_NEITHER, FILE_READ_ACCESS)
/* Read the Usn Record for a file */
#define FSCTL_READ_FILE_USN_DATA \
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 58, METHOD_NEITHER, FILE_READ_ACCESS)
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 58, METHOD_NEITHER, FILE_ANY_ACCESS)
/* Generate Close Usn Record */
#define FSCTL_WRITE_USN_CLOSE_RECORD \
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 59, METHOD_NEITHER, FILE_READ_ACCESS)
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 59, METHOD_NEITHER, FILE_ANY_ACCESS)
#define FSCTL_EXTEND_VOLUME \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 60, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_SIS_COPYFILE \
@@ -465,8 +460,7 @@ extern "C" {
#define FSCTL_RECALL_FILE \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 69, METHOD_NEITHER, FILE_ANY_ACCESS)
#define FSCTL_SET_DEFECT_MANAGEMENT \
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 77, \
- METHOD_BUFFERED, FILE_WRITE_ACCESS)
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 77, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define FSCTL_QUERY_SPARING_INFO \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 78, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_QUERY_ON_DISK_VOLUME_INFO \
@@ -475,6 +469,17 @@ extern "C" {
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 101, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_SET_SHORT_NAME_BEHAVIOR \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 109, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_FILE_LEVEL_TRIM \
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 130, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define FSCTL_OFFLOAD_READ \
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 153, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define FSCTL_OFFLOAD_WRITE \
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 154, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define FSCTL_GET_INTEGRITY_INFORMATION \
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 159, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_SET_INTEGRITY_INFORMATION \
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 160, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
/* FILE_DEVICE_NETWORK_FILE_SYSTEM */
/* Read the snapshot info for Volume Shadow Copy Services */
@@ -485,12 +490,12 @@ extern "C" {
#define FSCTL_SRV_REQUEST_RESUME_KEY \
CTL_CODE(FILE_DEVICE_NETWORK_FILE_SYSTEM, 0x1e, \
METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define FSCTL_SRV_LMR_GET_LINK_TRACKING_INFORMATION \
+#define FSCTL_LMR_GET_LINK_TRACKING_INFORMATION \
CTL_CODE(FILE_DEVICE_NETWORK_FILE_SYSTEM, 0x3a, \
- METHOD_BUFFERED, FILE_ACCESS_ANY)
-#define FSCTL_SRV_LMR_SET_LINK_TRACKING_INFORMATION \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_LMR_SET_LINK_TRACKING_INFORMATION \
CTL_CODE(FILE_DEVICE_NETWORK_FILE_SYSTEM, 0x3b, \
- METHOD_BUFFERED, FILE_ACCESS_ANY)
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
/* server-side data movement */
#define FSCTL_SRV_COPYCHUNK \
CTL_CODE(FILE_DEVICE_NETWORK_FILE_SYSTEM, 0x3c, \
@@ -507,23 +512,31 @@ extern "C" {
METHOD_NEITHER, FILE_READ_ACCESS)
#define FSCTL_SRV_UNKNOWN_x71 \
CTL_CODE(FILE_DEVICE_NETWORK_FILE_SYSTEM, 0x71, \
- METHOD_BUFFERED, FILE_ACCESS_ANY)
-#define FSCTL_SRV_LMR_REQUEST_RESILIENCY \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_LMR_REQUEST_RESILIENCY \
CTL_CODE(FILE_DEVICE_NETWORK_FILE_SYSTEM, 0x75, \
METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_QUERY_NETWORK_INTERFACE_INFO \
+ CTL_CODE(FILE_DEVICE_NETWORK_FILE_SYSTEM, 0x7f, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_VALIDATE_NEGOTIATE_INFO \
+ CTL_CODE(FILE_DEVICE_NETWORK_FILE_SYSTEM, 0x81, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
/* FILE_DEVICE_DFS */
#define FSCTL_DFS_GET_REFERRALS \
- CTL_CODE(FILE_DEVICE_DFS, 0x65, METHOD_BUFFERED, FILE_ACCESS_ANY)
+ CTL_CODE(FILE_DEVICE_DFS, 0x65, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_DFS_GET_REFERRALS_EX \
+ CTL_CODE(FILE_DEVICE_DFS, 0x6c, METHOD_BUFFERED, FILE_ANY_ACCESS)
/* FILE_DEVICE_NAMED_PIPE */
#define FSCTL_PIPE_PEEK \
- CTL_CODE(FILE_DEVICE_NAMED_PIPE, 0x3, METHOD_BUFFERED, FILE_READ_ACCESS)
+ CTL_CODE(FILE_DEVICE_NAMED_PIPE, 3, METHOD_BUFFERED, FILE_READ_ACCESS)
#define FSCTL_PIPE_TRANSCEIVE \
- CTL_CODE(FILE_DEVICE_NAMED_PIPE, 0x5, \
- METHOD_NEITHER, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+ CTL_CODE(FILE_DEVICE_NAMED_PIPE, 5, METHOD_NEITHER, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define FSCTL_PIPE_WAIT \
- CTL_CODE(FILE_DEVICE_NAMED_PIPE, 0x6, METHOD_BUFFERED, FILE_ANY_ACCESS)
+ CTL_CODE(FILE_DEVICE_NAMED_PIPE, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
#endif /* _FILESYSTEMFSCTL_ */
diff --git a/usr/src/uts/intel/smbsrv/Makefile b/usr/src/uts/intel/smbsrv/Makefile
index 7ebb6d4cbe..5dfe45a19c 100644
--- a/usr/src/uts/intel/smbsrv/Makefile
+++ b/usr/src/uts/intel/smbsrv/Makefile
@@ -21,7 +21,9 @@
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
-# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+#
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+#
#
# This makefile drives the production of the cifs server file system
@@ -69,7 +71,6 @@ CLEANFILES += $(MODSTUBS_O)
INC_PATH += -I$(SRC)/common
-CERRWARN += -_gcc=-Wno-uninitialized
CERRWARN += -_gcc=-Wno-parentheses
CERRWARN += -_gcc=-Wno-switch
diff --git a/usr/src/uts/sparc/smbsrv/Makefile b/usr/src/uts/sparc/smbsrv/Makefile
index 5d3a78bf44..7e51079dea 100644
--- a/usr/src/uts/sparc/smbsrv/Makefile
+++ b/usr/src/uts/sparc/smbsrv/Makefile
@@ -21,7 +21,9 @@
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
-# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+#
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+#
#
# This makefile drives the production of the cifs server file system
@@ -68,7 +70,6 @@ CLEANFILES += $(MODSTUBS_O)
INC_PATH += -I$(SRC)/common
-CERRWARN += -_gcc=-Wno-uninitialized
CERRWARN += -_gcc=-Wno-parentheses
CERRWARN += -_gcc=-Wno-switch