diff options
author | Gordon Ross <gwr@nexenta.com> | 2013-06-26 16:05:44 -0400 |
---|---|---|
committer | Gordon Ross <gwr@nexenta.com> | 2015-11-06 12:22:30 -0500 |
commit | a90cf9f29973990687fa61de9f1f6ea22e924e40 (patch) | |
tree | 42f77e59c4470ef6d2ba3118ba6c75e43686c398 | |
parent | b24e356b384ccc80805e7150979de2373d44347c (diff) | |
download | illumos-gate-a90cf9f29973990687fa61de9f1f6ea22e924e40.tar.gz |
6399 SMB2 support
Portions contributed by: Alek Pinchuk <alek@nexenta.com>
Portions contributed by: Kevin Crowe <kevin.crowe@nexenta.com>
Portions contributed by: Matt Barden <Matt.Barden@nexenta.com>
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Daniel Borek <daniel.borek@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Yuri Pankov <Yuri.Pankov@nexenta.com>
Approved by: Garrett D'Amore <garrett@damore.org>
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("abuf, &xa->req_data_mb, - mb_offset, bytes_left); - - quota = kmem_zalloc(sizeof (smb_quota_t), KM_SLEEP); - - if (smb_mbc_decodef("abuf, "llqqqq", - &next_offset, &sidlen, &mtime, - "a->q_used, "a->q_thresh, "a->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("abuf, 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("abuf, "llqqqq", + &next_offset, &sidlen, &mtime, + "a->q_used, "a->q_thresh, "a->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 |