diff options
author | Gordon Ross <gwr@nexenta.com> | 2017-09-07 17:38:42 -0400 |
---|---|---|
committer | Gordon Ross <gwr@nexenta.com> | 2019-08-18 12:49:34 -0400 |
commit | 8d94f651a44d41a7147253bb5dad1a53941e8f50 (patch) | |
tree | cba3775c8f1f6ef216013772f9d391f1a4ff0297 | |
parent | 2f57b5e005e6dce9d124b3dbd5fdcad1cc0372d2 (diff) | |
download | illumos-gate-8d94f651a44d41a7147253bb5dad1a53941e8f50.tar.gz |
11031 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Jason King <jason.king@joyent.com>
Approved by: Garrett D'Amore <garrett@damore.org>
43 files changed, 2225 insertions, 723 deletions
diff --git a/exception_lists/packaging b/exception_lists/packaging index caa19a9102..8fb6466e79 100644 --- a/exception_lists/packaging +++ b/exception_lists/packaging @@ -600,6 +600,7 @@ usr/lib/smbsrv/libfksmbsrv.so.1 usr/lib/smbsrv/libmlsvc.so usr/lib/smbsrv/libsmb.so usr/lib/smbsrv/libsmbns.so +usr/lib/smbsrv/nvlprint usr/lib/smbsrv/test-msgbuf usr/lib/smbsrv/testoplock # diff --git a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c index b54549eebb..4195a62149 100644 --- a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c +++ b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c @@ -1623,6 +1623,9 @@ tree_flag_bits[] = { { "FORCE_L2_OPLOCK", SMB_TREE_FORCE_L2_OPLOCK, SMB_TREE_FORCE_L2_OPLOCK }, + { "CA", + SMB_TREE_CA, + SMB_TREE_CA }, { NULL, 0, 0 } }; @@ -2334,17 +2337,26 @@ smb_kshare_walk_step(mdb_walk_state_t *wsp) * ***************************************************************************** */ +typedef struct mdb_smb_vfs { + list_node_t sv_lnd; + uint32_t sv_magic; + uint32_t sv_refcnt; + vfs_t *sv_vfsp; + vnode_t *sv_rootvp; +} mdb_smb_vfs_t; + struct smb_vfs_cb_args { uint_t opts; vnode_t vn; char path[MAXPATHLEN]; }; +/*ARGSUSED*/ static int smb_vfs_cb(uintptr_t addr, const void *data, void *varg) { struct smb_vfs_cb_args *args = varg; - const smb_vfs_t *sf = data; + mdb_smb_vfs_t sf; if (args->opts & SMB_OPT_VERBOSE) { mdb_arg_t argv; @@ -2363,16 +2375,21 @@ smb_vfs_cb(uintptr_t addr, const void *data, void *varg) * * Get the vnode v_path string if we can. */ + if (mdb_ctf_vread(&sf, SMBSRV_SCOPE "smb_vfs_t", + "mdb_smb_vfs_t", addr, 0) < 0) { + mdb_warn("failed to read struct smb_vfs at %p", addr); + return (DCMD_ERR); + } strcpy(args->path, "?"); if (mdb_vread(&args->vn, sizeof (args->vn), - (uintptr_t)sf->sv_rootvp) == sizeof (args->vn)) + (uintptr_t)sf.sv_rootvp) == sizeof (args->vn)) (void) mdb_readstr(args->path, sizeof (args->path), (uintptr_t)args->vn.v_path); mdb_printf("%-?p ", addr); - mdb_printf("%-10d ", sf->sv_refcnt); - mdb_printf("%-?p ", sf->sv_vfsp); - mdb_printf("%-?p ", sf->sv_rootvp); + mdb_printf("%-10d ", sf.sv_refcnt); + mdb_printf("%-?p ", sf.sv_vfsp); + mdb_printf("%-?p ", sf.sv_rootvp); mdb_printf("%-s\n", args->path); return (WALK_NEXT); @@ -2442,7 +2459,12 @@ smb_vfs_walk_init(mdb_walk_state_t *wsp) * OFFSETOF(smb_server_t, sv_export.e_vfs_list.ll_list); */ GET_OFFSET(sv_exp_off, smb_server_t, sv_export); - GET_OFFSET(ex_vfs_off, smb_export_t, e_vfs_list); + /* GET_OFFSET(ex_vfs_off, smb_export_t, e_vfs_list); */ + ex_vfs_off = mdb_ctf_offsetof_by_name("smb_export_t", "e_vfs_list"); + if (ex_vfs_off < 0) { + mdb_warn("cannot lookup: smb_export_t .e_vfs_list"); + return (WALK_ERR); + } GET_OFFSET(ll_off, smb_llist_t, ll_list); wsp->walk_addr += (sv_exp_off + ex_vfs_off + ll_off); diff --git a/usr/src/cmd/smbsrv/Makefile b/usr/src/cmd/smbsrv/Makefile index 8e7699c252..85d9ec05f1 100644 --- a/usr/src/cmd/smbsrv/Makefile +++ b/usr/src/cmd/smbsrv/Makefile @@ -26,7 +26,7 @@ # SUBDIRS = smbadm smbd smbstat dtrace fksmbd bind-helper \ - test-msgbuf testoplock + nvlprint testoplock test-msgbuf MSGSUBDIRS = smbadm smbstat include ../Makefile.cmd diff --git a/usr/src/cmd/smbsrv/fksmbd/fksmbd_shr.c b/usr/src/cmd/smbsrv/fksmbd/fksmbd_shr.c index 23038f1641..20f1f146b0 100644 --- a/usr/src/cmd/smbsrv/fksmbd/fksmbd_shr.c +++ b/usr/src/cmd/smbsrv/fksmbd/fksmbd_shr.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -115,6 +115,8 @@ smb_shr_load(void *args) */ new_share("test", "/var/smb/test", "fksmbd test share", SMB_SHRF_GUEST_OK); + new_share("testca", "/var/smb/test", "fksmbd test CA share", + SMB_SHRF_CA); /* Allow creating lots of shares for testing. */ shr_file = getenv("FKSMBD_SHARE_FILE"); diff --git a/usr/src/cmd/smbsrv/nvlprint/Makefile b/usr/src/cmd/smbsrv/nvlprint/Makefile new file mode 100644 index 0000000000..6e107f4219 --- /dev/null +++ b/usr/src/cmd/smbsrv/nvlprint/Makefile @@ -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 2017 Nexenta Systems, Inc. All rights reserved. +# + + +PROG= nvlprint + +include ../../Makefile.cmd +ROOTCMDDIR= $(ROOT)/usr/lib/smbsrv + +CFLAGS += $(CCVERBOSE) + +CPPFLAGS += -D_FILE_OFFSET_BITS=64 +LDLIBS += -lnvpair + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTCMD) + +clean: + +lint: + +include ../../Makefile.targ diff --git a/usr/src/cmd/smbsrv/nvlprint/nvlprint.c b/usr/src/cmd/smbsrv/nvlprint/nvlprint.c new file mode 100644 index 0000000000..939cedd933 --- /dev/null +++ b/usr/src/cmd/smbsrv/nvlprint/nvlprint.c @@ -0,0 +1,88 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Print a packed nvlist from a file. + */ + +#include <stdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "libnvpair.h" + +char buf[65536]; + +void +dumpit(FILE *fp) +{ + struct stat st; + size_t flen; + int rlen; + nvlist_t *nvl = NULL; + int err; + + if (fstat(fileno(fp), &st) < 0) { + perror("fstat"); + return; + } + flen = (size_t)st.st_size; + if (flen > sizeof (buf)) { + (void) printf("File too large\n"); + return; + } + rlen = fread(buf, 1, flen, fp); + if (rlen <= 0) { + perror("fread"); + return; + } + if (rlen != flen) { + (void) printf("Short read %d %d \n", rlen, flen); + return; + } + + err = nvlist_unpack(buf, flen, &nvl, 0); + if (err != 0) { + (void) printf("nvlist_unpack, err=%d\n", err); + return; + } + + nvlist_print(stdout, nvl); + nvlist_free(nvl); +} + +int +main(int argc, char **argv) +{ + FILE *fp; + int i; + + if (argc < 2) { + (void) fprintf(stderr, "usage: %s {filename} [filename2...]\n", + argv[0]); + return (1); + } + for (i = 1; i < argc; i++) { + fp = fopen(argv[i], "r"); + if (fp == NULL) { + perror(argv[i]); + return (1); + } + (void) printf("%s:\n", argv[i]); + dumpit(fp); + (void) fclose(fp); + } + return (0); +} diff --git a/usr/src/lib/libfakekernel/common/clock.c b/usr/src/lib/libfakekernel/common/clock.c index 2bee02af2e..deacbd4705 100644 --- a/usr/src/lib/libfakekernel/common/clock.c +++ b/usr/src/lib/libfakekernel/common/clock.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ @@ -83,3 +83,48 @@ void scalehrtime(hrtime_t *t) { } + +/* + * These functions are blatently stolen from the kernel. + * See the dissertation in the comments preceding the + * hrt2ts() and ts2hrt() functions in: + * uts/common/os/timers.c + */ +void +hrt2ts(hrtime_t hrt, timespec_t *tsp) +{ + uint32_t sec, nsec, tmp; + + tmp = (uint32_t)(hrt >> 30); + sec = tmp - (tmp >> 2); + sec = tmp - (sec >> 5); + sec = tmp + (sec >> 1); + sec = tmp - (sec >> 6) + 7; + sec = tmp - (sec >> 3); + sec = tmp + (sec >> 1); + sec = tmp + (sec >> 3); + sec = tmp + (sec >> 4); + tmp = (sec << 7) - sec - sec - sec; + tmp = (tmp << 7) - tmp - tmp - tmp; + tmp = (tmp << 7) - tmp - tmp - tmp; + nsec = (uint32_t)hrt - (tmp << 9); + while (nsec >= NANOSEC) { + nsec -= NANOSEC; + sec++; + } + tsp->tv_sec = (time_t)sec; + tsp->tv_nsec = nsec; +} + +hrtime_t +ts2hrt(const timestruc_t *tsp) +{ + hrtime_t hrt; + + hrt = tsp->tv_sec; + hrt = (hrt << 7) - hrt - hrt - hrt; + hrt = (hrt << 7) - hrt - hrt - hrt; + hrt = (hrt << 7) - hrt - hrt - hrt; + hrt = (hrt << 9) + tsp->tv_nsec; + return (hrt); +} diff --git a/usr/src/lib/libfakekernel/common/kmisc.c b/usr/src/lib/libfakekernel/common/kmisc.c index 15730d6539..70f303e035 100644 --- a/usr/src/lib/libfakekernel/common/kmisc.c +++ b/usr/src/lib/libfakekernel/common/kmisc.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. * Copyright 2017 RackTop Systems. */ @@ -95,6 +95,7 @@ highbit64(uint64_t i) int ddi_strtoul(const char *str, char **endp, int base, unsigned long *res) { + errno = 0; *res = strtoul(str, endp, base); if (*res == 0) return (errno); diff --git a/usr/src/lib/libfakekernel/common/mapfile-vers b/usr/src/lib/libfakekernel/common/mapfile-vers index e8d231159c..c7f3ae1d1d 100644 --- a/usr/src/lib/libfakekernel/common/mapfile-vers +++ b/usr/src/lib/libfakekernel/common/mapfile-vers @@ -99,7 +99,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { highbit; highbit64; - + hrt2ts; hz; issig; @@ -231,6 +231,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { tick_per_msec; timeout; + ts2hrt; tsignal; uiomove; uioskip; diff --git a/usr/src/lib/libshare/smb/libshare_smb.c b/usr/src/lib/libshare/smb/libshare_smb.c index e15bb26d9a..f567e7818b 100644 --- a/usr/src/lib/libshare/smb/libshare_smb.c +++ b/usr/src/lib/libshare/smb/libshare_smb.c @@ -179,6 +179,7 @@ struct option_defs optdefs[] = { { SHOPT_GUEST, OPT_TYPE_BOOLEAN }, { SHOPT_DFSROOT, OPT_TYPE_BOOLEAN }, { SHOPT_DESCRIPTION, OPT_TYPE_STRING }, + { SHOPT_CA, OPT_TYPE_BOOLEAN }, { SHOPT_FSO, OPT_TYPE_BOOLEAN }, { SHOPT_QUOTAS, OPT_TYPE_BOOLEAN }, { SHOPT_ENCRYPT, OPT_TYPE_STRING }, @@ -2195,6 +2196,9 @@ smb_build_shareinfo(sa_share_t share, sa_resource_t resource, smb_share_t *si) if (smb_saprop_getbool(opts, SHOPT_DFSROOT, B_FALSE)) si->shr_flags |= SMB_SHRF_DFSROOT; + if (smb_saprop_getbool(opts, SHOPT_CA, B_FALSE)) + si->shr_flags |= SMB_SHRF_CA; + if (smb_saprop_getbool(opts, SHOPT_FSO, B_FALSE)) si->shr_flags |= SMB_SHRF_FSO; diff --git a/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com b/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com index 507122dadd..7f29003239 100644 --- a/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com +++ b/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com @@ -119,7 +119,6 @@ OBJS_FS_SMBSRV = \ smb_tree_connect.o \ smb_unlock_byte_range.o \ smb_user.o \ - smb_vfs.o \ smb_vops.o \ smb_vss.o \ smb_write.o \ @@ -210,8 +209,10 @@ STRIP_STABS = : # Note: need our sys includes _before_ ENVCPPFLAGS, proto etc. +# Also, like Makefile.uts, reset CPPFLAGS CPPFLAGS.first += -I../../../libfakekernel/common CPPFLAGS.first += -I../common +CPPFLAGS = $(CPPFLAGS.first) INCS += -I$(SRC)/uts/common INCS += -I$(SRC)/common/smbsrv diff --git a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_cred.c b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_cred.c index 7b2bb93581..030c9c6244 100644 --- a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_cred.c +++ b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_cred.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2016 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #include <sys/types.h> @@ -53,6 +53,14 @@ smb_cred_create(smb_token_t *token) return (cr); } +cred_t * +smb_kcred_create(void) +{ + cred_t *cr; + cr = CRED(); + return (cr); +} + void smb_user_setcred(smb_user_t *user, cred_t *cr, uint32_t privileges) { diff --git a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_init.c b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_init.c index 4f0d6bf299..dc9eff1b44 100644 --- a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_init.c +++ b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_init.c @@ -141,9 +141,12 @@ fksmbsrv_drv_open(void) int fksmbsrv_drv_close(void) { + smb_server_t *sv; int rc; - rc = smb_server_delete(); + rc = smb_server_lookup(&sv); + if (rc == 0) + rc = smb_server_delete(sv); if (g_init_done != 0) { smb_server_g_fini(); diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c b/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c index ccd5b75c12..8a354a7da0 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c @@ -770,6 +770,10 @@ smb_shr_modify(smb_share_t *new_si) si->shr_flags &= ~SMB_SHRF_DFSROOT; si->shr_flags |= flag; + flag = (new_si->shr_flags & SMB_SHRF_CA); + si->shr_flags &= ~SMB_SHRF_CA; + si->shr_flags |= flag; + flag = (new_si->shr_flags & SMB_SHRF_FSO); si->shr_flags &= ~SMB_SHRF_FSO; si->shr_flags |= flag; @@ -1822,6 +1826,12 @@ smb_shr_sa_get(sa_share_t share, sa_resource_t resource, smb_share_t *si) free(val); } + val = smb_shr_sa_getprop(opts, SHOPT_CA); + if (val != NULL) { + smb_shr_sa_setflag(val, si, SMB_SHRF_CA); + free(val); + } + val = smb_shr_sa_getprop(opts, SHOPT_FSO); if (val != NULL) { smb_shr_sa_setflag(val, si, SMB_SHRF_FSO); @@ -2611,6 +2621,8 @@ smb_shr_encode(smb_share_t *si, nvlist_t **nvlist) rc |= nvlist_add_string(smb, SHOPT_GUEST, "true"); if ((si->shr_flags & SMB_SHRF_DFSROOT) != 0) rc |= nvlist_add_string(smb, SHOPT_DFSROOT, "true"); + if ((si->shr_flags & SMB_SHRF_CA) != 0) + rc |= nvlist_add_string(smb, SHOPT_CA, "true"); if ((si->shr_flags & SMB_SHRF_FSO) != 0) rc |= nvlist_add_string(smb, SHOPT_FSO, "true"); if ((si->shr_flags & SMB_SHRF_QUOTAS) != 0) diff --git a/usr/src/tools/quick/make-smbsrv b/usr/src/tools/quick/make-smbsrv index 9e2381288d..0aabee3812 100755 --- a/usr/src/tools/quick/make-smbsrv +++ b/usr/src/tools/quick/make-smbsrv @@ -278,6 +278,7 @@ usr/lib/libmlrpc.so.2 usr/lib/smbsrv/libmlsvc.so.1 usr/lib/smbsrv/libsmb.so.1 usr/lib/smbsrv/libsmbns.so.1 +usr/lib/smbsrv/nvlprint usr/lib/smbsrv/smbd usr/sbin/smbadm usr/sbin/smbstat diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 34b97128c1..ebc1b2db90 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1193,7 +1193,6 @@ SMBSRV_OBJS += $(SMBSRV_SHARED_OBJS) \ smb_tree_connect.o \ smb_unlock_byte_range.o \ smb_user.o \ - smb_vfs.o \ smb_vops.o \ smb_vss.o \ smb_write.o \ diff --git a/usr/src/uts/common/fs/smbsrv/smb2_close.c b/usr/src/uts/common/fs/smbsrv/smb2_close.c index e019a3c3da..bbb000f329 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_close.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_close.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2017 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -71,6 +71,8 @@ smb2_close(smb_request_t *sr) } } + if (of->dh_persist) + smb2_dh_setdoc_persistent(of); smb_ofile_close(of, 0); errout: diff --git a/usr/src/uts/common/fs/smbsrv/smb2_create.c b/usr/src/uts/common/fs/smbsrv/smb2_create.c index 6aab3c5127..582efbae28 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_create.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_create.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2017 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -280,7 +280,6 @@ smb2_create(smb_request_t *sr) * many create context types are ignored too. */ op->dh_vers = SMB2_NOT_DURABLE; - op->dh_v2_flags = 0; if ((cctx.cc_in_flags & (CCTX_DH_RECONNECT|CCTX_DH_RECONNECT_V2)) != 0) { @@ -388,6 +387,9 @@ smb2_create(smb_request_t *sr) cctx.cc_in_flags &= ~CCTX_REQUEST_LEASE; } + if ((sr->tid_tree->t_flags & SMB_TREE_CA) == 0) + op->dh_v2_flags &= ~DH_PERSISTENT; + if ((cctx.cc_in_flags & (CCTX_DH_REQUEST|CCTX_DH_REQUEST_V2)) != 0) { if ((cctx.cc_in_flags & CCTX_DH_REQUEST_V2) != 0) @@ -441,15 +443,19 @@ smb2_create(smb_request_t *sr) * non-durable handles in case we get the ioctl * to set "resiliency" on this handle. */ - if (of->f_ftype == SMB_FTYPE_DISK) - smb_ofile_set_persistid(of); + if (of->f_ftype == SMB_FTYPE_DISK) { + if ((op->dh_v2_flags & DH_PERSISTENT) != 0) + smb_ofile_set_persistid_ph(of); + else + smb_ofile_set_persistid_dh(of); + } /* * [MS-SMB2] 3.3.5.9.8 * Handling the SMB2_CREATE_REQUEST_LEASE Create Context */ if ((cctx.cc_in_flags & CCTX_REQUEST_LEASE) != 0) { - status = smb2_lease_create(sr); + status = smb2_lease_create(sr, sr->session->clnt_uuid); if (status != NT_STATUS_SUCCESS) { if (op->action_taken == SMB_OACT_CREATED) { smb_ofile_set_delete_on_close(sr, of); @@ -479,7 +485,8 @@ smb2_create(smb_request_t *sr) if ((cctx.cc_in_flags & (CCTX_DH_REQUEST|CCTX_DH_REQUEST_V2)) != 0 && smb_node_is_file(of->f_node) && - ((op->op_oplock_level == SMB2_OPLOCK_LEVEL_BATCH) || + ((op->dh_v2_flags & DH_PERSISTENT) != 0 || + (op->op_oplock_level == SMB2_OPLOCK_LEVEL_BATCH) || (op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE && (op->lease_state & OPLOCK_LEVEL_CACHE_HANDLE) != 0))) { /* @@ -489,8 +496,13 @@ smb2_create(smb_request_t *sr) (void) memcpy(of->dh_create_guid, op->create_guid, UUID_LEN); - /* no persistent handles yet */ - of->dh_persist = B_FALSE; + if ((op->dh_v2_flags & DH_PERSISTENT) != 0) { + if (smb2_dh_make_persistent(sr, of) == 0) { + of->dh_persist = B_TRUE; + } else { + op->dh_v2_flags = 0; + } + } } if (op->dh_vers != SMB2_NOT_DURABLE) { uint32_t msto; @@ -503,8 +515,11 @@ smb2_create(smb_request_t *sr) * the default timeout (in mSec.) */ msto = op->dh_timeout; - if (msto == 0) - msto = smb2_dh_def_timeout; + if (msto == 0) { + msto = (of->dh_persist) ? + smb2_persist_timeout : + smb2_dh_def_timeout; + } if (msto > smb2_dh_max_timeout) msto = smb2_dh_max_timeout; op->dh_timeout = msto; @@ -512,6 +527,7 @@ smb2_create(smb_request_t *sr) } } else { op->dh_vers = SMB2_NOT_DURABLE; + op->dh_v2_flags = 0; } /* diff --git a/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c b/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c index b592dc4c5f..88c4b6d600 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c @@ -979,6 +979,16 @@ cmd_done: */ if (!sr->smb2_async && sr->smb2_next_command != 0) goto cmd_start; + + /* + * If we have a durable handle, and this operation updated + * the nvlist, write it out (before smb2_send_reply). + */ + if (sr->dh_nvl_dirty) { + sr->dh_nvl_dirty = B_FALSE; + smb2_dh_update_nvfile(sr); + } + smb2_send_reply(sr); if (sr->smb2_async && sr->smb2_next_command != 0) { MBC_FLUSH(&sr->reply); /* New reply buffer. */ @@ -990,6 +1000,9 @@ cleanup: if (disconnect) smb_session_disconnect(session); + /* + * Do "postwork" for oplock (and maybe other things) + */ if (sr->sr_postwork != NULL) smb2sr_run_postwork(sr); @@ -1728,6 +1741,16 @@ smb2sr_run_postwork(smb_request_t *top_sr) default: ASSERT(0); } + + /* + * If we have a durable handle, and this operation + * updated the nvlist, write it out. + */ + if (post_sr->dh_nvl_dirty) { + post_sr->dh_nvl_dirty = B_FALSE; + smb2_dh_update_nvfile(post_sr); + } + post_sr->sr_state = SMB_REQ_STATE_COMPLETED; smb_request_free(post_sr); } diff --git a/usr/src/uts/common/fs/smbsrv/smb2_durable.c b/usr/src/uts/common/fs/smbsrv/smb2_durable.c index 9ba3dd9c07..7b65924ca4 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_durable.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_durable.c @@ -21,6 +21,7 @@ #include <sys/cmn_err.h> #include <sys/fcntl.h> #include <sys/nbmlock.h> +#include <sys/sid.h> #include <smbsrv/string.h> #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_fsops.h> @@ -53,6 +54,48 @@ uint32_t smb2_dh_max_timeout = 300 * MILLISEC; /* mSec. */ uint32_t smb2_res_def_timeout = 120 * MILLISEC; /* mSec. */ uint32_t smb2_res_max_timeout = 300 * MILLISEC; /* mSec. */ +uint32_t smb2_persist_timeout = 300 * MILLISEC; /* mSec. */ + +/* Max. size of the file used to store a CA handle. */ +static uint32_t smb2_dh_max_cah_size = 64 * 1024; +static uint32_t smb2_ca_info_version = 1; + +/* + * Want this to have invariant layout on disk, where the + * last two uint32_t values are stored as a uint64_t + */ +struct nvlk { + uint64_t lk_start; + uint64_t lk_len; + /* (lk_pid << 32) | lk_type */ +#ifdef _BIG_ENDIAN + uint32_t lk_pid, lk_type; +#else + uint32_t lk_type, lk_pid; +#endif +}; + +static void smb2_dh_import_share(void *); +static smb_ofile_t *smb2_dh_import_handle(smb_request_t *, smb_node_t *, + uint64_t); +static int smb2_dh_read_nvlist(smb_request_t *, smb_node_t *, struct nvlist **); +static int smb2_dh_import_cred(smb_ofile_t *, char *); + +#define DH_SN_SIZE 24 /* size of DH stream name buffers */ +/* + * Build the stream name used to store a CA handle. + * i.e. ":0123456789abcdef:$CA" + * Note: smb_fsop_create adds the SUNWsmb prefix, + * so we compose the name without the prefix. + */ +static inline void +smb2_dh_make_stream_name(char *buf, size_t buflen, uint64_t id) +{ + ASSERT(buflen >= DH_SN_SIZE); + (void) snprintf(buf, buflen, + ":%016" PRIx64 ":$CA", id); +} + /* * smb_dh_should_save * @@ -80,6 +123,11 @@ uint32_t smb2_res_max_timeout = 300 * MILLISEC; /* mSec. */ * Open.OplockState == Held, and Open.IsDurable is TRUE. * * - Open.IsPersistent is TRUE. + * + * We also deal with some special cases for shutdown of the + * server, session, user, tree (in that order). Other than + * the cases above, shutdown (or forced termination) should + * destroy durable handles. */ boolean_t smb_dh_should_save(smb_ofile_t *of) @@ -87,12 +135,49 @@ smb_dh_should_save(smb_ofile_t *of) ASSERT(MUTEX_HELD(&of->f_mutex)); ASSERT(of->dh_vers != SMB2_NOT_DURABLE); - if (of->f_user->preserve_opens == SMB2_DH_PRESERVE_NONE) + /* SMB service shutting down, destroy DH */ + if (of->f_server->sv_state == SMB_SERVER_STATE_STOPPING) return (B_FALSE); - if (of->f_user->preserve_opens == SMB2_DH_PRESERVE_ALL) + /* + * SMB Session (connection) going away (server up). + * If server initiated disconnect, destroy DH + * If client initiated disconnect, save all DH. + */ + if (of->f_session->s_state == SMB_SESSION_STATE_TERMINATED) + return (B_FALSE); + if (of->f_session->s_state == SMB_SESSION_STATE_DISCONNECTED) return (B_TRUE); + /* + * SMB User logoff, session still "up". + * Action depends on why/how this logoff happened, + * determined based on user->preserve_opens + */ + if (of->f_user->u_state == SMB_USER_STATE_LOGGING_OFF) { + switch (of->f_user->preserve_opens) { + case SMB2_DH_PRESERVE_NONE: + /* Server-initiated */ + return (B_FALSE); + case SMB2_DH_PRESERVE_SOME: + /* Previous session logoff. */ + goto preserve_some; + case SMB2_DH_PRESERVE_ALL: + /* Protocol logoff request */ + return (B_TRUE); + } + } + + /* + * SMB tree disconnecting (user still logged on) + * i.e. when kshare export forces disconnection. + */ + if (of->f_tree->t_state == SMB_TREE_STATE_DISCONNECTING) + return (B_FALSE); + +preserve_some: + /* preserve_opens == SMB2_DH_PRESERVE_SOME */ + switch (of->dh_vers) { case SMB2_RESILIENT: return (B_TRUE); @@ -116,6 +201,1063 @@ smb_dh_should_save(smb_ofile_t *of) } /* + * Is this stream name a CA handle? i.e. + * ":0123456789abcdef:$CA" + */ +static boolean_t +smb2_dh_match_ca_name(const char *name, uint64_t *idp) +{ + static const char suffix[] = ":$CA"; + u_longlong_t ull; + const char *p = name; + char *p2 = NULL; + int len, rc; + + if (*p++ != ':') + return (B_FALSE); + + rc = ddi_strtoull(p, &p2, 16, &ull); + if (rc != 0 || p2 != (p + 16)) + return (B_FALSE); + p += 16; + + len = sizeof (suffix) - 1; + if (strncmp(p, suffix, len) != 0) + return (B_FALSE); + p += len; + + if (*p != '\0') + return (B_FALSE); + + *idp = (uint64_t)ull; + return (B_TRUE); +} + +/* + * smb2_dh_new_ca_share + * + * Called when a new share has ca=true. Find or create the CA dir, + * and start a thread to import persistent handles. + */ +int +smb2_dh_new_ca_share(smb_server_t *sv, smb_kshare_t *shr) +{ + smb_kshare_t *shr2; + smb_request_t *sr; + + ASSERT(STYPE_ISDSK(shr->shr_type)); + + /* + * Need to lookup the kshare again, to get a hold. + * Add a function to just get the hold? + */ + shr2 = smb_kshare_lookup(sv, shr->shr_name); + if (shr2 != shr) + return (EINVAL); + + sr = smb_request_alloc(sv->sv_session, 0); + if (sr == NULL) { + /* shutting down? */ + smb_kshare_release(sv, shr); + return (EINTR); + } + sr->sr_state = SMB_REQ_STATE_SUBMITTED; + + /* + * Mark this share as "busy importing persistent handles" + * so we can hold off tree connect until that's done. + * Will clear and wakeup below. + */ + mutex_enter(&shr->shr_mutex); + shr->shr_import_busy = sr; + mutex_exit(&shr->shr_mutex); + + /* + * Start a taskq job to import any CA handles. + * The hold on the kshare is given to this job, + * which releases it when it's done. + */ + sr->arg.tcon.si = shr; /* hold from above */ + (void) taskq_dispatch( + sv->sv_worker_pool, + smb2_dh_import_share, sr, TQ_SLEEP); + + return (0); +} + +int smb2_dh_import_delay = 0; + +static void +smb2_dh_import_share(void *arg) +{ + smb_request_t *sr = arg; + smb_kshare_t *shr = sr->arg.tcon.si; + smb_node_t *snode; + cred_t *kcr = zone_kcred(); + smb_streaminfo_t *str_info = NULL; + uint64_t id; + smb_node_t *str_node; + smb_odir_t *od = NULL; + smb_ofile_t *of; + int rc; + boolean_t eof; + + sr->sr_state = SMB_REQ_STATE_ACTIVE; + + if (smb2_dh_import_delay > 0) + delay(SEC_TO_TICK(smb2_dh_import_delay)); + + /* + * Borrow the server's "root" user. + * + * This takes the place of smb_session_lookup_ssnid() + * that would happen in smb2_dispatch for a normal SR. + * As usual, this hold is released in smb_request_free. + */ + sr->uid_user = sr->sr_server->sv_rootuser; + smb_user_hold_internal(sr->uid_user); + sr->user_cr = sr->uid_user->u_cred; + + /* + * Create a temporary tree connect + */ + sr->arg.tcon.path = shr->shr_name; + sr->tid_tree = smb_tree_alloc(sr, shr, shr->shr_root_node, + ACE_ALL_PERMS, 0); + if (sr->tid_tree == NULL) { + cmn_err(CE_NOTE, "smb2_dh_import_share: " + "failed connect share <%s>", shr->shr_name); + goto out; + } + snode = sr->tid_tree->t_snode; + + /* + * Get the buffers we'll use to read CA handle data. + * Stash in sr_request_buf for smb2_dh_import_handle(). + * Also a buffer for the stream name info. + */ + sr->sr_req_length = smb2_dh_max_cah_size; + sr->sr_request_buf = kmem_alloc(sr->sr_req_length, KM_SLEEP); + str_info = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP); + + /* + * Open the ext. attr dir under the share root and + * import CA handles for this share. + */ + if (smb_odir_openat(sr, snode, &od) != 0) { + cmn_err(CE_NOTE, "Share [%s] CA import, no xattr dir?", + shr->shr_name); + goto out; + } + + eof = B_FALSE; + do { + /* + * If the kshare gets unshared before we finish, + * bail out so we don't hold things up. + */ + if (shr->shr_flags & SMB_SHRF_REMOVED) + break; + + /* + * Read a stream name and info + */ + rc = smb_odir_read_streaminfo(sr, od, str_info, &eof); + if ((rc != 0) || (eof)) + break; + + /* + * Skip anything not a CA handle. + */ + if (!smb2_dh_match_ca_name(str_info->si_name, &id)) { + continue; + } + + /* + * Lookup stream node and import + */ + str_node = NULL; + rc = smb_fsop_lookup_name(sr, kcr, SMB_CASE_SENSITIVE, + snode, snode, str_info->si_name, &str_node); + if (rc != 0) { + cmn_err(CE_NOTE, "Share [%s] CA import, " + "lookup <%s> failed rc=%d", + shr->shr_name, str_info->si_name, rc); + continue; + } + of = smb2_dh_import_handle(sr, str_node, id); + smb_node_release(str_node); + if (of != NULL) { + smb_ofile_release(of); + of = NULL; + } + sr->fid_ofile = NULL; + + } while (!eof); + +out: + if (od != NULL) { + smb_odir_close(od); + smb_odir_release(od); + } + + if (str_info != NULL) + kmem_free(str_info, sizeof (smb_streaminfo_t)); + /* Let smb_request_free clean up sr->sr_request_buf */ + + /* + * We did a (temporary, internal) tree connect above, + * which we need to undo before we return. Note that + * smb_request_free will do the final release of + * sr->tid_tree, sr->uid_user + */ + if (sr->tid_tree != NULL) + smb_tree_disconnect(sr->tid_tree, B_FALSE); + + /* + * Wake up any waiting tree connect(s). + * See smb_tree_connect_disk(). + */ + mutex_enter(&shr->shr_mutex); + shr->shr_import_busy = NULL; + cv_broadcast(&shr->shr_cv); + mutex_exit(&shr->shr_mutex); + + smb_kshare_release(sr->sr_server, shr); + smb_request_free(sr); +} + +/* + * This returns the new ofile mostly for dtrace. + */ +static smb_ofile_t * +smb2_dh_import_handle(smb_request_t *sr, smb_node_t *str_node, + uint64_t persist_id) +{ + uint8_t client_uuid[UUID_LEN]; + smb_tree_t *tree = sr->tid_tree; + smb_arg_open_t *op = &sr->arg.open; + smb_pathname_t *pn = &op->fqi.fq_path; + cred_t *kcr = zone_kcred(); + struct nvlist *nvl = NULL; + char *sidstr = NULL; + smb_ofile_t *of = NULL; + smb_attr_t *pa; + boolean_t did_open = B_FALSE; + boolean_t have_lease = B_FALSE; + hrtime_t hrt; + uint64_t *u64p; + uint64_t u64; + uint32_t u32; + uint32_t status; + char *s; + uint8_t *u8p; + uint_t alen; + int rc; + + /* + * While we're called with arg.tcon, we now want to use + * smb_arg_open for the rest of import, so clear it. + */ + bzero(op, sizeof (*op)); + op->create_disposition = FILE_OPEN; + + /* + * Read and unpack the NVL + */ + rc = smb2_dh_read_nvlist(sr, str_node, &nvl); + if (rc != 0) + return (NULL); + + /* + * Known CA info version? + */ + u32 = 0; + rc = nvlist_lookup_uint32(nvl, "info_version", &u32); + if (rc != 0 || u32 != smb2_ca_info_version) { + cmn_err(CE_NOTE, "CA import (%s/%s) bad vers=%d", + tree->t_resource, str_node->od_name, u32); + goto errout; + } + + /* + * The persist ID in the nvlist should match the one + * encoded in the file name. (not enforced) + */ + u64 = 0; + rc = nvlist_lookup_uint64(nvl, "file_persistid", &u64); + if (rc != 0 || u64 != persist_id) { + cmn_err(CE_WARN, "CA import (%s/%s) bad id=%016" PRIx64, + tree->t_resource, str_node->od_name, u64); + /* goto errout? (allow) */ + } + + /* + * Does it belong in the share being imported? + */ + s = NULL; + rc = nvlist_lookup_string(nvl, "share_name", &s); + if (rc != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) no share_name", + tree->t_resource, str_node->od_name); + goto errout; + } + if (smb_strcasecmp(s, tree->t_sharename, 0) != 0) { + /* Normal (not an error) */ +#ifdef DEBUG + cmn_err(CE_NOTE, "CA import (%s/%s) other share", + tree->t_resource, str_node->od_name); +#endif + goto errout; + } + + /* + * Get the path name (for lookup) + */ + rc = nvlist_lookup_string(nvl, "path_name", &pn->pn_path); + if (rc != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) no path_name", + tree->t_resource, str_node->od_name); + goto errout; + } + + /* + * owner sid + */ + rc = nvlist_lookup_string(nvl, "owner_sid", &sidstr); + if (rc != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) no owner_sid", + tree->t_resource, str_node->od_name); + goto errout; + } + + /* + * granted access + */ + rc = nvlist_lookup_uint32(nvl, + "granted_access", &op->desired_access); + if (rc != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) no granted_access", + tree->t_resource, str_node->od_name); + goto errout; + } + + /* + * share access + */ + rc = nvlist_lookup_uint32(nvl, + "share_access", &op->share_access); + if (rc != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) no share_access", + tree->t_resource, str_node->od_name); + goto errout; + } + + /* + * create options + */ + rc = nvlist_lookup_uint32(nvl, + "create_options", &op->create_options); + if (rc != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) no create_options", + tree->t_resource, str_node->od_name); + goto errout; + } + + /* + * create guid (client-assigned) + */ + alen = UUID_LEN; + u8p = NULL; + rc = nvlist_lookup_uint8_array(nvl, "file_guid", &u8p, &alen); + if (rc != 0 || alen != UUID_LEN) { + cmn_err(CE_NOTE, "CA import (%s/%s) bad file_guid", + tree->t_resource, str_node->od_name); + goto errout; + } + bcopy(u8p, op->create_guid, UUID_LEN); + + /* + * client uuid (identifies the client) + */ + alen = UUID_LEN; + u8p = NULL; + rc = nvlist_lookup_uint8_array(nvl, "client_uuid", &u8p, &alen); + if (rc != 0 || alen != UUID_LEN) { + cmn_err(CE_NOTE, "CA import (%s/%s) no client_uuid", + tree->t_resource, str_node->od_name); + goto errout; + } + bcopy(u8p, client_uuid, UUID_LEN); + + /* + * Lease key (optional) + */ + alen = SMB_LEASE_KEY_SZ; + u8p = NULL; + rc = nvlist_lookup_uint8_array(nvl, "lease_uuid", &u8p, &alen); + if (rc == 0) { + bcopy(u8p, op->lease_key, UUID_LEN); + (void) nvlist_lookup_uint32(nvl, + "lease_state", &op->lease_state); + (void) nvlist_lookup_uint16(nvl, + "lease_epoch", &op->lease_epoch); + (void) nvlist_lookup_uint16(nvl, + "lease_version", &op->lease_version); + have_lease = B_TRUE; + } else { + (void) nvlist_lookup_uint32(nvl, + "oplock_state", &op->op_oplock_state); + } + + /* + * Done getting what we need from the NV list. + * (re)open the file + */ + status = smb_common_open(sr); + if (status != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) open failed 0x%x", + tree->t_resource, str_node->od_name, status); + (void) smb_node_set_delete_on_close(str_node, kcr, 0); + goto errout; + } + of = sr->fid_ofile; + did_open = B_TRUE; + + /* + * Now restore the rest of the SMB2 level state. + * See smb2_create after smb_common_open + */ + + /* + * Setup of->f_cr with owner SID + */ + rc = smb2_dh_import_cred(of, sidstr); + if (rc != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) import cred failed", + tree->t_resource, str_node->od_name); + goto errout; + } + + /* + * Use the persist ID we previously assigned. + * Like smb_ofile_set_persistid_ph() + */ + rc = smb_ofile_insert_persistid(of, persist_id); + if (rc != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) " + "insert_persistid rc=%d", + tree->t_resource, str_node->od_name, rc); + goto errout; + } + + /* + * Like smb2_lease_create() + * + * Lease state is stored in each persistent handle, but + * only one handle has the state we want. As we import + * each handle, "upgrade" the lease if the handle we're + * importing has a "better" lease state (higher epoch or + * more cache rights). After all handles are imported, + * that will get the lease to the right state. + */ + if (have_lease) { + smb_lease_t *ls; + status = smb2_lease_create(sr, client_uuid); + if (status != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) get lease 0x%x", + tree->t_resource, str_node->od_name, status); + goto errout; + } + ls = of->f_lease; + + /* Use most current "epoch". */ + mutex_enter(&ls->ls_mutex); + if (ls->ls_epoch < op->lease_epoch) + ls->ls_epoch = op->lease_epoch; + mutex_exit(&ls->ls_mutex); + + /* + * Get the lease (and oplock) + * uses op->lease_state + */ + op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + smb2_lease_acquire(sr); + + } else { + /* + * No lease; maybe get an oplock + * uses: op->op_oplock_level + */ + if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) { + op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + } else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) { + op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) { + op->op_oplock_level = SMB2_OPLOCK_LEVEL_II; + } else { + op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; + } + smb2_oplock_acquire(sr); + } + + /* + * Byte range locks + */ + alen = 0; + u64p = NULL; + if (nvlist_lookup_uint64_array(nvl, "locks", &u64p, &alen) == 0) { + uint_t i; + uint_t nlocks = alen / 3; + struct nvlk *nlp; + + nlp = (struct nvlk *)u64p; + for (i = 0; i < nlocks; i++) { + status = smb_lock_range( + sr, + nlp->lk_start, + nlp->lk_len, + nlp->lk_pid, + nlp->lk_type, + 0); + if (status != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) " + "get lock %d failed 0x%x", + tree->t_resource, + str_node->od_name, + i, status); + } + nlp++; + } + } + alen = SMB_OFILE_LSEQ_MAX; + u8p = NULL; + if (nvlist_lookup_uint8_array(nvl, "lockseq", &u8p, &alen) == 0) { + if (alen != SMB_OFILE_LSEQ_MAX) { + cmn_err(CE_NOTE, "CA import (%s/%s) " + "get lockseq bad len=%d", + tree->t_resource, + str_node->od_name, + alen); + } else { + mutex_enter(&of->f_mutex); + bcopy(u8p, of->f_lock_seq, alen); + mutex_exit(&of->f_mutex); + } + } + + /* + * Optional "sticky" times (set pending attributes) + */ + mutex_enter(&of->f_mutex); + pa = &of->f_pending_attr; + if (nvlist_lookup_hrtime(nvl, "atime", &hrt) == 0) { + hrt2ts(hrt, &pa->sa_vattr.va_atime); + pa->sa_mask |= SMB_AT_ATIME; + } + if (nvlist_lookup_hrtime(nvl, "mtime", &hrt) == 0) { + hrt2ts(hrt, &pa->sa_vattr.va_mtime); + pa->sa_mask |= SMB_AT_MTIME; + } + if (nvlist_lookup_hrtime(nvl, "ctime", &hrt) == 0) { + hrt2ts(hrt, &pa->sa_vattr.va_ctime); + pa->sa_mask |= SMB_AT_CTIME; + } + mutex_exit(&of->f_mutex); + + /* + * Make durable and persistent. + * See smb2_dh_make_persistent() + */ + of->dh_vers = SMB2_DURABLE_V2; + bcopy(op->create_guid, of->dh_create_guid, UUID_LEN); + of->dh_persist = B_TRUE; + of->dh_nvfile = str_node; + smb_node_ref(str_node); + of->dh_nvlist = nvl; + nvl = NULL; + + /* + * Now make it state orphaned... + * See smb_ofile_drop(), then + * smb_ofile_save_dh() + */ + mutex_enter(&of->f_mutex); + of->f_state = SMB_OFILE_STATE_SAVE_DH; + of->dh_timeout_offset = MSEC2NSEC(smb2_persist_timeout); + mutex_exit(&of->f_mutex); + + /* + * Finished! + */ + return (of); + +errout: + if (did_open) { + smb_ofile_close(of, 0); + smb_ofile_release(of); + } else { + ASSERT(of == NULL); + } + + if (nvl != NULL) + nvlist_free(nvl); + + return (NULL); +} + +static int +smb2_dh_read_nvlist(smb_request_t *sr, smb_node_t *node, + struct nvlist **nvlpp) +{ + smb_attr_t attr; + iovec_t iov; + uio_t uio; + smb_kshare_t *shr = sr->arg.tcon.si; + cred_t *kcr = zone_kcred(); + size_t flen; + int rc; + + bzero(&attr, sizeof (attr)); + attr.sa_mask = SMB_AT_SIZE; + rc = smb_node_getattr(NULL, node, kcr, NULL, &attr); + if (rc != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) getattr rc=%d", + shr->shr_path, node->od_name, rc); + return (rc); + } + + if (attr.sa_vattr.va_size < 4 || + attr.sa_vattr.va_size > sr->sr_req_length) { + cmn_err(CE_NOTE, "CA import (%s/%s) bad size=%" PRIu64, + shr->shr_path, node->od_name, + (uint64_t)attr.sa_vattr.va_size); + return (EINVAL); + } + flen = (size_t)attr.sa_vattr.va_size; + + bzero(&uio, sizeof (uio)); + iov.iov_base = sr->sr_request_buf; + iov.iov_len = flen; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_resid = flen; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_extflg = UIO_COPY_DEFAULT; + rc = smb_fsop_read(sr, kcr, node, NULL, &uio); + if (rc != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) read, rc=%d", + shr->shr_path, node->od_name, rc); + return (rc); + } + if (uio.uio_resid != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) short read", + shr->shr_path, node->od_name); + return (EIO); + } + + rc = nvlist_unpack(sr->sr_request_buf, flen, nvlpp, KM_SLEEP); + if (rc != 0) { + cmn_err(CE_NOTE, "CA import (%s/%s) unpack, rc=%d", + shr->shr_path, node->od_name, rc); + return (rc); + } + + return (0); +} + +/* + * Setup a vestigial credential in of->f_cr just good enough for + * smb_is_same_user to determine if the caller owned this ofile. + * At reconnect, of->f_cr will be replaced with the caller's. + */ +static int +smb2_dh_import_cred(smb_ofile_t *of, char *sidstr) +{ +#ifdef _FAKE_KERNEL + _NOTE(ARGUNUSED(sidstr)) + /* fksmbd doesn't have real credentials. */ + of->f_cr = CRED(); + crhold(of->f_cr); +#else + char tmpstr[SMB_SID_STRSZ]; + ksid_t ksid; + cred_t *cr, *oldcr; + int rc; + + (void) strlcpy(tmpstr, sidstr, sizeof (tmpstr)); + bzero(&ksid, sizeof (ksid)); + + rc = smb_sid_splitstr(tmpstr, &ksid.ks_rid); + if (rc != 0) + return (rc); + cr = crget(); + + ksid.ks_domain = ksid_lookupdomain(tmpstr); + crsetsid(cr, &ksid, KSID_USER); + ksiddomain_hold(ksid.ks_domain); + crsetsid(cr, &ksid, KSID_OWNER); + + /* + * Just to avoid leaving the KSID_GROUP slot NULL, + * put the "everyone" SID there (S-1-1-0). + */ + ksid.ks_domain = ksid_lookupdomain("S-1-1"); + ksid.ks_rid = 0; + crsetsid(cr, &ksid, KSID_GROUP); + + oldcr = of->f_cr; + of->f_cr = cr; + if (oldcr != NULL) + crfree(oldcr); +#endif + + return (0); +} + +/* + * Set Delete-on-Close (DoC) on the persistent state file so it will be + * removed when the last ref. goes away (in smb2_dh_close_persistent). + * + * This is called in just two places: + * (1) SMB2_close request -- client tells us to destroy the handle. + * (2) smb2_dh_expire -- client has forgotten about this handle. + * All other (server-initiated) close calls should leave these + * persistent state files in the file system. + */ +void +smb2_dh_setdoc_persistent(smb_ofile_t *of) +{ + smb_node_t *strnode; + uint32_t status; + + mutex_enter(&of->dh_nvlock); + if ((strnode = of->dh_nvfile) != NULL) + smb_node_ref(strnode); + mutex_exit(&of->dh_nvlock); + + if (strnode != NULL) { + status = smb_node_set_delete_on_close(strnode, + zone_kcred(), SMB_CASE_SENSITIVE); + if (status != 0) { + cmn_err(CE_WARN, "Can't set DoC on CA file: %s", + strnode->od_name); + DTRACE_PROBE1(rm__ca__err, smb_ofile_t *, of); + } + smb_node_release(strnode); + } +} + +/* + * During ofile close, free the persistent handle state nvlist and + * drop our reference to the state file node (which may unlink it + * if smb2_dh_setdoc_persistent was called). + */ +void +smb2_dh_close_persistent(smb_ofile_t *of) +{ + smb_node_t *strnode; + struct nvlist *nvl; + + /* + * Clear out nvlist and stream linkage + */ + mutex_enter(&of->dh_nvlock); + strnode = of->dh_nvfile; + of->dh_nvfile = NULL; + nvl = of->dh_nvlist; + of->dh_nvlist = NULL; + mutex_exit(&of->dh_nvlock); + + if (nvl != NULL) + nvlist_free(nvl); + + if (strnode != NULL) + smb_node_release(strnode); +} + +/* + * Make this durable handle persistent. + * If we succeed, set of->dh_persist = TRUE. + */ +int +smb2_dh_make_persistent(smb_request_t *sr, smb_ofile_t *of) +{ + char fname[DH_SN_SIZE]; + char sidstr[SMB_SID_STRSZ]; + smb_attr_t attr; + smb_arg_open_t *op = &sr->arg.open; + cred_t *kcr = zone_kcred(); + smb_node_t *dnode = of->f_tree->t_snode; + smb_node_t *fnode = NULL; + ksid_t *ksid; + int rc; + + ASSERT(of->dh_nvfile == NULL); + + /* + * Create the persistent handle nvlist file. + * It's a named stream in the share root. + */ + smb2_dh_make_stream_name(fname, sizeof (fname), of->f_persistid); + + bzero(&attr, sizeof (attr)); + attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE | SMB_AT_SIZE; + attr.sa_vattr.va_type = VREG; + attr.sa_vattr.va_mode = 0640; + attr.sa_vattr.va_size = 4; + rc = smb_fsop_create(sr, kcr, dnode, fname, &attr, &fnode); + if (rc != 0) + return (rc); + + mutex_enter(&of->dh_nvlock); + + /* fnode is held. rele in smb2_dh_close_persistent */ + of->dh_nvfile = fnode; + (void) nvlist_alloc(&of->dh_nvlist, NV_UNIQUE_NAME, KM_SLEEP); + + /* + * Want the ksid as a string + */ + ksid = crgetsid(of->f_user->u_cred, KSID_USER); + (void) snprintf(sidstr, sizeof (sidstr), "%s-%u", + ksid->ks_domain->kd_name, ksid->ks_rid); + + /* + * Fill in the fixed parts of the nvlist + */ + (void) nvlist_add_uint32(of->dh_nvlist, + "info_version", smb2_ca_info_version); + (void) nvlist_add_string(of->dh_nvlist, + "owner_sid", sidstr); + (void) nvlist_add_string(of->dh_nvlist, + "share_name", of->f_tree->t_sharename); + (void) nvlist_add_uint64(of->dh_nvlist, + "file_persistid", of->f_persistid); + (void) nvlist_add_uint8_array(of->dh_nvlist, + "file_guid", of->dh_create_guid, UUID_LEN); + (void) nvlist_add_string(of->dh_nvlist, + "client_ipaddr", sr->session->ip_addr_str); + (void) nvlist_add_uint8_array(of->dh_nvlist, + "client_uuid", sr->session->clnt_uuid, UUID_LEN); + (void) nvlist_add_string(of->dh_nvlist, + "path_name", op->fqi.fq_path.pn_path); + (void) nvlist_add_uint32(of->dh_nvlist, + "granted_access", of->f_granted_access); + (void) nvlist_add_uint32(of->dh_nvlist, + "share_access", of->f_share_access); + (void) nvlist_add_uint32(of->dh_nvlist, + "create_options", of->f_create_options); + if (of->f_lease != NULL) { + smb_lease_t *ls = of->f_lease; + (void) nvlist_add_uint8_array(of->dh_nvlist, + "lease_uuid", ls->ls_key, 16); + (void) nvlist_add_uint32(of->dh_nvlist, + "lease_state", ls->ls_state); + (void) nvlist_add_uint16(of->dh_nvlist, + "lease_epoch", ls->ls_epoch); + (void) nvlist_add_uint16(of->dh_nvlist, + "lease_version", ls->ls_version); + } else { + (void) nvlist_add_uint32(of->dh_nvlist, + "oplock_state", of->f_oplock.og_state); + } + mutex_exit(&of->dh_nvlock); + + smb2_dh_update_locks(sr, of); + + /* Tell sr update nvlist file */ + sr->dh_nvl_dirty = B_TRUE; + + return (0); +} + +void +smb2_dh_update_nvfile(smb_request_t *sr) +{ + smb_attr_t attr; + iovec_t iov; + uio_t uio; + smb_ofile_t *of = sr->fid_ofile; + cred_t *kcr = zone_kcred(); + char *buf = NULL; + size_t buflen = 0; + uint32_t wcnt; + int rc; + + if (of == NULL || of->dh_persist == B_FALSE) + return; + + mutex_enter(&of->dh_nvlock); + if (of->dh_nvlist == NULL || of->dh_nvfile == NULL) { + mutex_exit(&of->dh_nvlock); + return; + } + + rc = nvlist_size(of->dh_nvlist, &buflen, NV_ENCODE_XDR); + if (rc != 0) + goto out; + buf = kmem_zalloc(buflen, KM_SLEEP); + + rc = nvlist_pack(of->dh_nvlist, &buf, &buflen, + NV_ENCODE_XDR, KM_SLEEP); + if (rc != 0) + goto out; + + bzero(&attr, sizeof (attr)); + attr.sa_mask = SMB_AT_SIZE; + attr.sa_vattr.va_size = buflen; + rc = smb_node_setattr(sr, of->dh_nvfile, kcr, NULL, &attr); + if (rc != 0) + goto out; + + bzero(&uio, sizeof (uio)); + iov.iov_base = (void *) buf; + iov.iov_len = buflen; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_resid = buflen; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_extflg = UIO_COPY_DEFAULT; + rc = smb_fsop_write(sr, kcr, of->dh_nvfile, + NULL, &uio, &wcnt, 0); + if (rc == 0 && wcnt != buflen) + rc = EIO; + +out: + mutex_exit(&of->dh_nvlock); + + if (rc != 0) { + cmn_err(CE_WARN, + "clnt(%s) failed to update persistent handle, rc=%d", + sr->session->ip_addr_str, rc); + } + + if (buf != NULL) { + kmem_free(buf, buflen); + } +} + +/* + * Called after f_oplock (and lease) changes + * If lease, update: lease_state, lease_epoch + * else (oplock) update: oplock_state + */ +void +smb2_dh_update_oplock(smb_request_t *sr, smb_ofile_t *of) +{ + smb_lease_t *ls; + + mutex_enter(&of->dh_nvlock); + if (of->dh_nvlist == NULL) { + mutex_exit(&of->dh_nvlock); + return; + } + + if (of->f_lease != NULL) { + ls = of->f_lease; + (void) nvlist_add_uint32(of->dh_nvlist, + "lease_state", ls->ls_state); + (void) nvlist_add_uint16(of->dh_nvlist, + "lease_epoch", ls->ls_epoch); + } else { + (void) nvlist_add_uint32(of->dh_nvlist, + "oplock_state", of->f_oplock.og_state); + } + mutex_exit(&of->dh_nvlock); + + sr->dh_nvl_dirty = B_TRUE; +} + +/* + * Save locks from this ofile as an array of uint64_t, where the + * elements are triplets: (start, length, (pid << 32) | type) + * Note pid should always be zero for SMB2, so we could use + * that 32-bit spot for something else if needed. + */ +void +smb2_dh_update_locks(smb_request_t *sr, smb_ofile_t *of) +{ + uint8_t lseq[SMB_OFILE_LSEQ_MAX]; + smb_node_t *node = of->f_node; + smb_llist_t *llist = &node->n_lock_list; + size_t vec_sz; // storage size + uint_t my_cnt = 0; + uint64_t *vec = NULL; + struct nvlk *nlp; + smb_lock_t *lock; + + smb_llist_enter(llist, RW_READER); + vec_sz = (llist->ll_count + 1) * sizeof (struct nvlk); + vec = kmem_alloc(vec_sz, KM_SLEEP); + nlp = (struct nvlk *)vec; + for (lock = smb_llist_head(llist); + lock != NULL; + lock = smb_llist_next(llist, lock)) { + if (lock->l_file != of) + continue; + nlp->lk_start = lock->l_start; + nlp->lk_len = lock->l_length; + nlp->lk_pid = lock->l_pid; + nlp->lk_type = lock->l_type; + nlp++; + my_cnt++; + } + smb_llist_exit(llist); + + mutex_enter(&of->f_mutex); + bcopy(of->f_lock_seq, lseq, sizeof (lseq)); + mutex_exit(&of->f_mutex); + + mutex_enter(&of->dh_nvlock); + if (of->dh_nvlist != NULL) { + + (void) nvlist_add_uint64_array(of->dh_nvlist, + "locks", vec, my_cnt * 3); + + (void) nvlist_add_uint8_array(of->dh_nvlist, + "lockseq", lseq, sizeof (lseq)); + } + mutex_exit(&of->dh_nvlock); + + kmem_free(vec, vec_sz); + + sr->dh_nvl_dirty = B_TRUE; +} + +/* + * Save "sticky" times + */ +void +smb2_dh_update_times(smb_request_t *sr, smb_ofile_t *of, smb_attr_t *attr) +{ + hrtime_t t; + + mutex_enter(&of->dh_nvlock); + if (of->dh_nvlist == NULL) { + mutex_exit(&of->dh_nvlock); + return; + } + + if (attr->sa_mask & SMB_AT_ATIME) { + t = ts2hrt(&attr->sa_vattr.va_atime); + (void) nvlist_add_hrtime(of->dh_nvlist, "atime", t); + } + if (attr->sa_mask & SMB_AT_MTIME) { + t = ts2hrt(&attr->sa_vattr.va_mtime); + (void) nvlist_add_hrtime(of->dh_nvlist, "mtime", t); + } + if (attr->sa_mask & SMB_AT_CTIME) { + t = ts2hrt(&attr->sa_vattr.va_ctime); + (void) nvlist_add_hrtime(of->dh_nvlist, "ctime", t); + } + mutex_exit(&of->dh_nvlock); + + sr->dh_nvl_dirty = B_TRUE; +} + + +/* * Requirements for ofile found during reconnect (MS-SMB2 3.3.5.9.7): * - security descriptor must match provided descriptor * @@ -332,6 +1474,8 @@ smb2_dh_expire(void *arg) { smb_ofile_t *of = (smb_ofile_t *)arg; + if (of->dh_persist) + smb2_dh_setdoc_persistent(of); smb_ofile_close(of, 0); smb_ofile_release(of); } @@ -383,9 +1527,96 @@ smb2_durable_timers(smb_server_t *sv) } /* + * This is called when we're about to add a new open to some node. + * If we still have orphaned durable handles on this node, let's + * assume the client has lost interest in those and close them, + * otherwise we might conflict with our own orphaned handles. + * + * We need this because we import persistent handles "speculatively" + * during share import (before the client ever asks for reconnect). + * That allows us to avoid any need for a "create blackout" (or + * "grace period") because the imported handles prevent unwanted + * conflicting opens from other clients. However, if some client + * "forgets" about a persistent handle (*cough* Hyper-V) and tries + * a new (conflicting) open instead of a reconnect, that might + * fail unless we expire our orphaned durables handle first. + * + * Logic similar to smb_node_open_check() + */ +void +smb2_dh_close_my_orphans(smb_request_t *sr, smb_ofile_t *new_of) +{ + smb_node_t *node = new_of->f_node; + smb_ofile_t *of; + + SMB_NODE_VALID(node); + + smb_llist_enter(&node->n_ofile_list, RW_READER); + for (of = smb_llist_head(&node->n_ofile_list); + of != NULL; + of = smb_llist_next(&node->n_ofile_list, of)) { + + /* Same client? */ + if (of->f_lease != NULL && + bcmp(sr->session->clnt_uuid, + of->f_lease->ls_clnt, 16) != 0) + continue; + + if (!smb_is_same_user(sr->user_cr, of->f_cr)) + continue; + + mutex_enter(&of->f_mutex); + if (of->f_state == SMB_OFILE_STATE_ORPHANED) { + of->f_state = SMB_OFILE_STATE_EXPIRED; + /* inline smb_ofile_hold_internal() */ + of->f_refcnt++; + smb_llist_post(&node->n_ofile_list, + of, smb2_dh_expire); + } + mutex_exit(&of->f_mutex); + } + + smb_llist_exit(&node->n_ofile_list); +} + +/* + * Called for each orphaned DH during shutdown. + * Clean out any in-memory state, but leave any + * on-disk persistent handle state in place. + */ +static void +smb2_dh_cleanup(void *arg) +{ + smb_ofile_t *of = (smb_ofile_t *)arg; + smb_node_t *strnode; + struct nvlist *nvl; + + /* + * Intentionally skip smb2_dh_close_persistent by + * clearing dh_nvfile before smb_ofile_close(). + */ + mutex_enter(&of->dh_nvlock); + strnode = of->dh_nvfile; + of->dh_nvfile = NULL; + nvl = of->dh_nvlist; + of->dh_nvlist = NULL; + mutex_exit(&of->dh_nvlock); + + if (nvl != NULL) + nvlist_free(nvl); + + if (strnode != NULL) + smb_node_release(strnode); + + smb_ofile_close(of, 0); + smb_ofile_release(of); +} + +/* * Clean out durable handles during shutdown. - * Like, smb2_durable_timers but expire all, - * and make sure the hash buckets are empty. + * + * Like, smb2_durable_timers but cleanup only in-memory state, + * and leave any persistent state there for later reconnect. */ void smb2_dh_shutdown(smb_server_t *sv) @@ -410,7 +1641,7 @@ smb2_dh_shutdown(smb_server_t *sv) of->f_state = SMB_OFILE_STATE_EXPIRED; /* inline smb_ofile_hold_internal() */ of->f_refcnt++; - smb_llist_post(bucket, of, smb2_dh_expire); + smb_llist_post(bucket, of, smb2_dh_cleanup); break; default: break; diff --git a/usr/src/uts/common/fs/smbsrv/smb2_lease.c b/usr/src/uts/common/fs/smbsrv/smb2_lease.c index d2bf4805b3..95d7d9c7f1 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_lease.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_lease.c @@ -122,11 +122,10 @@ smb_hash_uuid(const uint8_t *uuid) * Handling the SMB2_CREATE_REQUEST_LEASE Create Context */ uint32_t -smb2_lease_create(smb_request_t *sr) +smb2_lease_create(smb_request_t *sr, uint8_t *clnt) { smb_arg_open_t *op = &sr->arg.open; uint8_t *key = op->lease_key; - uint8_t *clnt = sr->session->clnt_uuid; smb_ofile_t *of = sr->fid_ofile; smb_hash_t *ht = sr->sr_server->sv_lease_ht; smb_llist_t *bucket; diff --git a/usr/src/uts/common/fs/smbsrv/smb2_lock.c b/usr/src/uts/common/fs/smbsrv/smb2_lock.c index c6e8236cce..cc05f96e75 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_lock.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_lock.c @@ -142,6 +142,10 @@ smb2_lock(smb_request_t *sr) status = smb2_locks(sr); } + if (sr->fid_ofile->dh_persist) { + smb2_dh_update_locks(sr, sr->fid_ofile); + } + errout: sr->smb2_status = status; DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr); diff --git a/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c b/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c index cbdd5f9fb5..5bc7b01260 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c @@ -26,8 +26,12 @@ uint32_t smb2srv_capabilities = SMB2_CAP_DFS | SMB2_CAP_LEASING | SMB2_CAP_LARGE_MTU | + SMB2_CAP_PERSISTENT_HANDLES | SMB2_CAP_ENCRYPTION; +/* These are the only capabilities defined for SMB2.X */ +#define SMB_2X_CAPS (SMB2_CAP_DFS | SMB2_CAP_LEASING | SMB2_CAP_LARGE_MTU) + /* * These are not intended as customer tunables, but dev. & test folks * might want to adjust them (with caution). @@ -350,16 +354,26 @@ smb2_negotiate_common(smb_request_t *sr, uint16_t version) /* * [MS-SMB2] 3.3.5.4 Receiving an SMB2 NEGOTIATE Request * - * Only set CAP_ENCRYPTION if this is 3.0 or 3.0.2 and - * the client has it set. + * The SMB2.x capabilities are returned without regard for + * what capabilities the client provided in the request. + * The SMB3.x capabilities returned are the traditional + * logical AND of server and client capabilities. + * + * One additional check: If KCF is missing something we + * require for encryption, turn off that capability. */ - - if (s->dialect < SMB_VERS_3_0 || - !SMB3_CLIENT_ENCRYPTS(sr) || - smb3_encrypt_init_mech(s) != 0) - s->srv_cap = smb2srv_capabilities & ~SMB2_CAP_ENCRYPTION; - else - s->srv_cap = smb2srv_capabilities; + if (s->dialect < SMB_VERS_3_0) { + /* SMB 2.x */ + s->srv_cap = smb2srv_capabilities & SMB_2X_CAPS; + } else { + /* SMB 3.0 or later */ + s->srv_cap = smb2srv_capabilities & + (SMB_2X_CAPS | s->capabilities); + if ((s->srv_cap & SMB2_CAP_ENCRYPTION) != 0 && + smb3_encrypt_init_mech(s) != 0) { + s->srv_cap &= ~SMB2_CAP_ENCRYPTION; + } + } /* * See notes above smb2_max_rwsize, smb2_old_rwsize diff --git a/usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c b/usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c index e11a8855f7..34a74f564b 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c @@ -19,6 +19,8 @@ #include <smbsrv/smb2_kproto.h> +#define SMB2_SHARE_CAP_CA SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY + smb_sdrc_t smb2_tree_connect(smb_request_t *sr) { @@ -114,6 +116,10 @@ smb2_tree_connect(smb_request_t *sr) ShareFlags = 0; Capabilities = 0; + if ((tree->t_flags & SMB_TREE_DFSROOT) != 0) + Capabilities |= SMB2_SHARE_CAP_DFS; + if ((tree->t_flags & SMB_TREE_CA) != 0) + Capabilities |= SMB2_SHARE_CAP_CA; /* * SMB2 Tree Connect reply 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 161f2790f6..0ef06a3c3e 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_common_open.c +++ b/usr/src/uts/common/fs/smbsrv/smb_common_open.c @@ -40,9 +40,6 @@ int smb_session_ofile_max = 32768; -static volatile uint32_t smb_fids = 0; -#define SMB_UNIQ_FID() atomic_inc_32_nv(&smb_fids) - extern uint32_t smb_is_executable(char *); static void smb_delete_new_object(smb_request_t *); static int smb_set_open_attributes(smb_request_t *, smb_ofile_t *); @@ -280,6 +277,7 @@ smb_common_open(smb_request_t *sr) boolean_t fnode_shrlk = B_FALSE; boolean_t did_open = B_FALSE; boolean_t did_break_handle = B_FALSE; + boolean_t did_cleanup_orphans = B_FALSE; /* Get out now if we've been cancelled. */ mutex_enter(&sr->sr_mutex); @@ -350,10 +348,9 @@ smb_common_open(smb_request_t *sr) /* * Most of IPC open is handled in smb_opipe_open() */ - uniq_fid = SMB_UNIQ_FID(); op->create_options = 0; of = smb_ofile_alloc(sr, op, NULL, SMB_FTYPE_MESG_PIPE, - tree_fid, uniq_fid); + tree_fid); tree_fid = 0; // given to the ofile status = smb_opipe_open(sr, of); smb_threshold_exit(&sv->sv_opipe_ct); @@ -450,13 +447,6 @@ smb_common_open(smb_request_t *sr) goto errout; } - /* - * The uniq_fid is a CIFS-server-wide unique identifier for an ofile - * which is used to uniquely identify open instances for the - * VFS share reservation and POSIX locks. - */ - uniq_fid = SMB_UNIQ_FID(); - if (last_comp_found) { smb_node_unlock(dnode); @@ -584,10 +574,14 @@ smb_common_open(smb_request_t *sr) * affect the sharing checks, and may delete the file due to * DELETE_ON_CLOSE. This may block, so set the file opening * count before oplock stuff. + * + * Need the "proposed" ofile (and its TargetOplockKey) for + * correct oplock break semantics. */ of = smb_ofile_alloc(sr, op, fnode, SMB_FTYPE_DISK, - tree_fid, uniq_fid); + tree_fid); tree_fid = 0; // given to the ofile + uniq_fid = of->f_uniqid; smb_node_inc_opening_count(fnode); opening_incr = B_TRUE; @@ -683,6 +677,22 @@ smb_common_open(smb_request_t *sr) } /* + * If we still have orphaned durable handles on this file, + * let's assume the client has lost interest in those and + * close them so they don't cause sharing violations. + * See longer comment at smb2_dh_close_my_orphans(). + */ + if (status == NT_STATUS_SHARING_VIOLATION && + sr->session->dialect >= SMB_VERS_2_BASE && + did_cleanup_orphans == B_FALSE) { + + did_cleanup_orphans = B_TRUE; + smb2_dh_close_my_orphans(sr, of); + + goto shrlock_again; + } + + /* * SMB1 expects a 1 sec. delay before returning a * sharing violation error. If breaking oplocks * above took less than a sec, wait some more. @@ -904,27 +914,17 @@ create: goto errout; } + /* Create done. */ smb_node_unlock(dnode); dnode_wlock = B_FALSE; created = B_TRUE; op->action_taken = SMB_OACT_CREATED; + /* Note: hold from create */ fnode = op->fqi.fq_fnode; fnode_held = B_TRUE; - smb_node_inc_opening_count(fnode); - opening_incr = B_TRUE; - - smb_node_wrlock(fnode); - fnode_wlock = B_TRUE; - - status = smb_fsop_shrlock(sr->user_cr, fnode, uniq_fid, - op->desired_access, op->share_access); - if (status != 0) - goto errout; - fnode_shrlk = B_TRUE; - if (max_requested) { smb_fsop_eaccess(sr, sr->user_cr, fnode, &max_allowed); op->desired_access |= max_allowed; @@ -937,6 +937,27 @@ create: */ op->desired_access |= (READ_CONTROL | FILE_READ_ATTRIBUTES); + /* Allocate the ofile and fill in most of it. */ + of = smb_ofile_alloc(sr, op, fnode, SMB_FTYPE_DISK, + tree_fid); + tree_fid = 0; // given to the ofile + uniq_fid = of->f_uniqid; + + smb_node_inc_opening_count(fnode); + opening_incr = B_TRUE; + + /* + * Share access checks... + */ + smb_node_wrlock(fnode); + fnode_wlock = B_TRUE; + + status = smb_fsop_shrlock(sr->user_cr, fnode, uniq_fid, + op->desired_access, op->share_access); + if (status != 0) + goto errout; + fnode_shrlk = B_TRUE; + /* * MS-FSA 2.1.5.1.1 * If the Oplock member of the DirectoryStream in @@ -951,9 +972,6 @@ create: * * The break never blocks, so ignore the return. */ - of = smb_ofile_alloc(sr, op, fnode, SMB_FTYPE_DISK, - tree_fid, uniq_fid); - tree_fid = 0; // given to the ofile (void) smb_oplock_break_PARENT(dnode, of); } @@ -1052,8 +1070,9 @@ create: errout: if (did_open) { smb_ofile_close(of, 0); - /* Don't also ofile_free */ + /* rele via sr->fid_ofile */ } else if (of != NULL) { + /* No other refs possible */ smb_ofile_free(of); } diff --git a/usr/src/uts/common/fs/smbsrv/smb_cred.c b/usr/src/uts/common/fs/smbsrv/smb_cred.c index f47f5e72a5..8431db4653 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_cred.c +++ b/usr/src/uts/common/fs/smbsrv/smb_cred.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 2017 Nexenta Systems, Inc. All rights reserved. */ /* @@ -172,3 +172,19 @@ smb_cred_set_sidlist(smb_ids_t *token_grps) return (lp); } + +/* + * Special variant of smb_cred_create() used when we need an + * SMB kcred (e.g. DH import). The returned cred must be + * from crget() so it can be passed to smb_user_setcred(). + */ +cred_t * +smb_kcred_create(void) +{ + cred_t *cr; + + cr = crget(); + ASSERT(cr != NULL); + + return (cr); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_fsops.c b/usr/src/uts/common/fs/smbsrv/smb_fsops.c index 6aa4074221..1b7c3a9fa9 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_fsops.c +++ b/usr/src/uts/common/fs/smbsrv/smb_fsops.c @@ -365,6 +365,9 @@ smb_fsop_create(smb_request_t *sr, cred_t *cr, smb_node_t *dnode, * because we want to set the UID and GID on the named * stream in this case for consistency with the (unnamed * stream) file (see comments for smb_vop_setattr()). + * + * Note that some stream "types" are "restricted" and only + * internal callers (cr == kcred) can create those. */ static int smb_fsop_create_stream(smb_request_t *sr, cred_t *cr, @@ -379,6 +382,9 @@ smb_fsop_create_stream(smb_request_t *sr, cred_t *cr, int rc = 0; boolean_t fcreate = B_FALSE; + if (cr != kcr && smb_strname_restricted(sname)) + return (EACCES); + /* Look up / create the unnamed stream, fname */ rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS, sr->tid_tree->t_snode, dnode, fname, &fnode); @@ -663,6 +669,9 @@ smb_fsop_mkdir( * It is assumed that a reference exists on snode coming into this routine. * * A null smb_request might be passed to this function. + * + * Note that some stream "types" are "restricted" and only + * internal callers (cr == kcred) can remove those. */ int smb_fsop_remove( @@ -698,6 +707,11 @@ smb_fsop_remove( sname = kmem_alloc(MAXNAMELEN, KM_SLEEP); if (dnode->flags & NODE_XATTR_DIR) { + if (cr != zone_kcred() && smb_strname_restricted(name)) { + rc = EACCES; + goto out; + } + fnode = dnode->n_dnode; rc = smb_vop_stream_remove(fnode->vp, name, flags, cr); @@ -709,6 +723,11 @@ smb_fsop_remove( } else if (smb_is_stream_name(name)) { smb_stream_parse_name(name, fname, sname); + if (cr != zone_kcred() && smb_strname_restricted(sname)) { + rc = EACCES; + goto out; + } + /* * Look up the unnamed stream (i.e. fname). * Unmangle processing will be done on fname @@ -719,9 +738,7 @@ smb_fsop_remove( sr->tid_tree->t_snode, dnode, fname, &fnode); if (rc != 0) { - kmem_free(fname, MAXNAMELEN); - kmem_free(sname, MAXNAMELEN); - return (rc); + goto out; } /* @@ -744,9 +761,7 @@ smb_fsop_remove( if (rc == ENOENT) { if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) || !smb_maybe_mangled(name)) { - kmem_free(fname, MAXNAMELEN); - kmem_free(sname, MAXNAMELEN); - return (rc); + goto out; } longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); @@ -776,6 +791,7 @@ smb_fsop_remove( } } +out: kmem_free(fname, MAXNAMELEN); kmem_free(sname, MAXNAMELEN); @@ -1609,6 +1625,9 @@ smb_fsop_statfs( * check is performed on the named stream in case it has been * quarantined. kcred is used to avoid issues with the permissions * set on the extended attribute file representing the named stream. + * + * Note that some stream "types" are "restricted" and only + * internal callers (cr == kcred) can access those. */ int smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode, @@ -1639,9 +1658,14 @@ smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode, unnamed_node = SMB_IS_STREAM(snode); if (unnamed_node) { + cred_t *kcr = zone_kcred(); + ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); + if (cr != kcr && smb_strname_restricted(snode->od_name)) + return (NT_STATUS_ACCESS_DENIED); + /* * Perform VREAD access check on the named stream in case it * is quarantined. kcred is passed to smb_vop_access so it @@ -1649,7 +1673,7 @@ smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode, */ if (faccess & (FILE_READ_DATA | FILE_EXECUTE)) { error = smb_vop_access(snode->vp, VREAD, - 0, NULL, zone_kcred()); + 0, NULL, kcr); if (error) return (NT_STATUS_ACCESS_DENIED); } diff --git a/usr/src/uts/common/fs/smbsrv/smb_init.c b/usr/src/uts/common/fs/smbsrv/smb_init.c index 88d804723e..f7e1739367 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_init.c +++ b/usr/src/uts/common/fs/smbsrv/smb_init.c @@ -247,7 +247,14 @@ smb_drv_open(dev_t *devp, int flag, int otyp, cred_t *cr) static int smb_drv_close(dev_t dev, int flag, int otyp, cred_t *credp) { - return (smb_server_delete()); + smb_server_t *sv; + int rc; + + rc = smb_server_lookup(&sv); + if (rc == 0) + rc = smb_server_delete(sv); + + return (rc); } /* ARGSUSED */ diff --git a/usr/src/uts/common/fs/smbsrv/smb_kshare.c b/usr/src/uts/common/fs/smbsrv/smb_kshare.c index ef501a949a..0bdd85ddba 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_kshare.c +++ b/usr/src/uts/common/fs/smbsrv/smb_kshare.c @@ -26,8 +26,9 @@ */ #include <smbsrv/smb_door.h> -#include <smbsrv/smb_kproto.h> #include <smbsrv/smb_ktypes.h> +#include <smbsrv/smb2_kproto.h> +#include <smbsrv/smb_kstat.h> typedef struct smb_unshare { list_node_t us_lnd; @@ -36,7 +37,6 @@ typedef struct smb_unshare { static kmem_cache_t *smb_kshare_cache_share; static kmem_cache_t *smb_kshare_cache_unexport; -kmem_cache_t *smb_kshare_cache_vfs; static int smb_kshare_cmp(const void *, const void *); static void smb_kshare_hold(const void *); @@ -294,7 +294,6 @@ smb_export_stop(smb_server_t *sv) mutex_exit(&sv->sv_export.e_mutex); smb_avl_destroy(&sv->sv_export.e_share_avl); - smb_vfs_rele_all(&sv->sv_export); } void @@ -305,18 +304,12 @@ smb_kshare_g_init(void) smb_kshare_cache_unexport = kmem_cache_create("smb_unexport_cache", sizeof (smb_unshare_t), 8, NULL, NULL, NULL, NULL, NULL, 0); - - smb_kshare_cache_vfs = kmem_cache_create("smb_vfs_cache", - sizeof (smb_vfs_t), 8, NULL, NULL, NULL, NULL, NULL, 0); } void smb_kshare_init(smb_server_t *sv) { - smb_llist_constructor(&sv->sv_export.e_vfs_list, sizeof (smb_vfs_t), - offsetof(smb_vfs_t, sv_lnd)); - smb_slist_constructor(&sv->sv_export.e_unexport_list, sizeof (smb_unshare_t), offsetof(smb_unshare_t, us_lnd)); } @@ -348,10 +341,6 @@ smb_kshare_fini(smb_server_t *sv) kmem_cache_free(smb_kshare_cache_unexport, ux); } smb_slist_destructor(&sv->sv_export.e_unexport_list); - - smb_vfs_rele_all(&sv->sv_export); - - smb_llist_destructor(&sv->sv_export.e_vfs_list); } void @@ -359,7 +348,6 @@ smb_kshare_g_fini(void) { kmem_cache_destroy(smb_kshare_cache_unexport); kmem_cache_destroy(smb_kshare_cache_share); - kmem_cache_destroy(smb_kshare_cache_vfs); } /* @@ -683,10 +671,8 @@ smb_kshare_release(smb_server_t *sv, smb_kshare_t *shr) /* * Add the given share in the specified server. - * If the share is a disk share, smb_vfs_hold() is - * invoked to ensure that there is a hold on the - * corresponding file system before the share is - * added to shares AVL. + * If the share is a disk share, lookup the share path + * and hold the smb_node_t for the share root. * * If the share is an Autohome share and it is * already in the AVL only a reference count for @@ -697,7 +683,7 @@ smb_kshare_export(smb_server_t *sv, smb_kshare_t *shr) { smb_avl_t *share_avl; smb_kshare_t *auto_shr; - vnode_t *vp; + smb_node_t *snode = NULL; int rc = 0; share_avl = &sv->sv_export.e_share_avl; @@ -712,36 +698,53 @@ smb_kshare_export(smb_server_t *sv, smb_kshare_t *shr) } if ((auto_shr = smb_avl_lookup(share_avl, shr)) != NULL) { - if ((auto_shr->shr_flags & SMB_SHRF_AUTOHOME) == 0) { - smb_avl_release(share_avl, auto_shr); - return (EEXIST); + rc = EEXIST; + if ((auto_shr->shr_flags & SMB_SHRF_AUTOHOME) != 0) { + mutex_enter(&auto_shr->shr_mutex); + auto_shr->shr_autocnt++; + mutex_exit(&auto_shr->shr_mutex); + rc = 0; } - - mutex_enter(&auto_shr->shr_mutex); - auto_shr->shr_autocnt++; - mutex_exit(&auto_shr->shr_mutex); smb_avl_release(share_avl, auto_shr); - return (0); + return (rc); } - if ((rc = smb_server_sharevp(sv, shr->shr_path, &vp)) != 0) { - cmn_err(CE_WARN, "export[%s(%s)]: failed obtaining vnode (%d)", + /* + * Get the root smb_node_t for this share, held. + * This hold is normally released during AVL destroy, + * via the element destructor: smb_kshare_destroy + */ + rc = smb_server_share_lookup(sv, shr->shr_path, &snode); + if (rc != 0) { + cmn_err(CE_WARN, "export[%s(%s)]: lookup failed (%d)", shr->shr_name, shr->shr_path, rc); return (rc); } - if ((rc = smb_vfs_hold(&sv->sv_export, vp->v_vfsp)) == 0) { - if ((rc = smb_avl_add(share_avl, shr)) != 0) { - cmn_err(CE_WARN, "export[%s]: failed caching (%d)", - shr->shr_name, rc); - smb_vfs_rele(&sv->sv_export, vp->v_vfsp); + shr->shr_root_node = snode; + if ((rc = smb_avl_add(share_avl, shr)) != 0) { + cmn_err(CE_WARN, "export[%s]: failed caching (%d)", + shr->shr_name, rc); + shr->shr_root_node = NULL; + smb_node_release(snode); + return (rc); + } + + /* + * For CA shares, find or create the CA handle dir, + * and (if restarted) import persistent handles. + */ + if ((shr->shr_flags & SMB_SHRF_CA) != 0) { + rc = smb2_dh_new_ca_share(sv, shr); + if (rc != 0) { + /* Just make it a non-CA share. */ + mutex_enter(&shr->shr_mutex); + shr->shr_flags &= ~SMB_SHRF_CA; + mutex_exit(&shr->shr_mutex); + rc = 0; } - } else { - cmn_err(CE_WARN, "export[%s(%s)]: failed holding VFS (%d)", - shr->shr_name, shr->shr_path, rc); } - VN_RELE(vp); return (rc); } @@ -763,8 +766,6 @@ smb_kshare_unexport(smb_server_t *sv, const char *shrname) smb_avl_t *share_avl; smb_kshare_t key; smb_kshare_t *shr; - vnode_t *vp; - int rc; boolean_t auto_unexport; share_avl = &sv->sv_export.e_share_avl; @@ -784,19 +785,12 @@ smb_kshare_unexport(smb_server_t *sv, const char *shrname) } } - if (STYPE_ISDSK(shr->shr_type)) { - if ((rc = smb_server_sharevp(sv, shr->shr_path, &vp)) != 0) { - smb_avl_release(share_avl, shr); - cmn_err(CE_WARN, "unexport[%s]: failed obtaining vnode" - " (%d)", shrname, rc); - return (rc); - } + smb_avl_remove(share_avl, shr); - smb_vfs_rele(&sv->sv_export, vp->v_vfsp); - VN_RELE(vp); - } + mutex_enter(&shr->shr_mutex); + shr->shr_flags |= SMB_SHRF_REMOVED; + mutex_exit(&shr->shr_mutex); - smb_avl_remove(share_avl, shr); smb_avl_release(share_avl, shr); return (0); @@ -891,6 +885,7 @@ smb_kshare_decode(nvlist_t *share) SMB_SHRF_DFSROOT); tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_QUOTAS, SMB_SHRF_QUOTAS); + tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_CA, SMB_SHRF_CA); tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_FSO, SMB_SHRF_FSO); tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_AUTOHOME, SMB_SHRF_AUTOHOME); @@ -1040,6 +1035,11 @@ smb_kshare_destroy(void *p) ASSERT(shr); ASSERT(shr->shr_magic == SMB_SHARE_MAGIC); + if (shr->shr_ca_dir != NULL) + smb_node_release(shr->shr_ca_dir); + if (shr->shr_root_node) + smb_node_release(shr->shr_root_node); + smb_mem_free(shr->shr_name); smb_mem_free(shr->shr_path); smb_mem_free(shr->shr_cmnt); diff --git a/usr/src/uts/common/fs/smbsrv/smb_node.c b/usr/src/uts/common/fs/smbsrv/smb_node.c index 63756f9037..3e9933d51a 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_node.c +++ b/usr/src/uts/common/fs/smbsrv/smb_node.c @@ -88,7 +88,7 @@ * course the state of the node should be tested/updated under the * protection of the mutex). */ -#include <smbsrv/smb_kproto.h> +#include <smbsrv/smb2_kproto.h> #include <smbsrv/smb_fsops.h> #include <smbsrv/smb_kstat.h> #include <sys/ddi.h> @@ -1574,10 +1574,20 @@ smb_node_setattr(smb_request_t *sr, smb_node_t *node, attr->sa_crtime; mutex_exit(&of->f_mutex); + /* * The f_pending_attr times are reapplied in * smb_ofile_close(). */ + + /* + * If this change is coming directly from a client + * (sr != NULL) and it's a persistent handle, save + * the "sticky times" in the handle. + */ + if (sr != NULL && of->dh_persist) { + smb2_dh_update_times(sr, of, attr); + } } if ((attr->sa_mask & SMB_AT_ALLOCSZ) != 0) { diff --git a/usr/src/uts/common/fs/smbsrv/smb_ofile.c b/usr/src/uts/common/fs/smbsrv/smb_ofile.c index 0142bf9164..531ca314fb 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_ofile.c +++ b/usr/src/uts/common/fs/smbsrv/smb_ofile.c @@ -280,11 +280,7 @@ #include <smbsrv/smb2_kproto.h> #include <smbsrv/smb_fsops.h> #include <sys/time.h> - -/* XXX: May need to actually assign GUIDs for these. */ -/* Don't leak object addresses */ -#define SMB_OFILE_PERSISTID(of) \ - ((uintptr_t)&smb_cache_ofile ^ (uintptr_t)(of)) +#include <sys/random.h> static boolean_t smb_ofile_is_open_locked(smb_ofile_t *); static void smb_ofile_delete(void *arg); @@ -296,6 +292,14 @@ static int smb_ofile_netinfo_init(smb_ofile_t *, smb_netfileinfo_t *); static void smb_ofile_netinfo_fini(smb_netfileinfo_t *); /* + * The uniq_fid is a CIFS-server-wide unique identifier for an ofile + * which is used to uniquely identify open instances for the + * VFS share reservation and POSIX locks. + */ +static volatile uint32_t smb_fids = 0; +#define SMB_UNIQ_FID() atomic_inc_32_nv(&smb_fids) + +/* * smb_ofile_alloc * Allocate an ofile and fill in it's "up" pointers, but * do NOT link it into the tree's list of ofiles or the @@ -304,6 +308,9 @@ static void smb_ofile_netinfo_fini(smb_netfileinfo_t *); * * If we don't get as far as smb_ofile_open with this OF, * call smb_ofile_free() to free this object. + * + * Note: The following sr members may be null during + * persistent handle import: session, uid_usr, tid_tree */ smb_ofile_t * smb_ofile_alloc( @@ -311,10 +318,10 @@ smb_ofile_alloc( smb_arg_open_t *op, smb_node_t *node, /* optional (may be NULL) */ uint16_t ftype, - uint16_t tree_fid, - uint32_t uniqid) + uint16_t tree_fid) { - smb_tree_t *tree = sr->tid_tree; + smb_user_t *user = sr->uid_user; /* optional */ + smb_tree_t *tree = sr->tid_tree; /* optional */ smb_ofile_t *of; of = kmem_cache_alloc(smb_cache_ofile, KM_SLEEP); @@ -324,22 +331,28 @@ smb_ofile_alloc( mutex_init(&of->f_mutex, NULL, MUTEX_DEFAULT, NULL); list_create(&of->f_notify.nc_waiters, sizeof (smb_request_t), offsetof(smb_request_t, sr_waiters)); + mutex_init(&of->dh_nvlock, NULL, MUTEX_DEFAULT, NULL); of->f_state = SMB_OFILE_STATE_ALLOC; of->f_refcnt = 1; of->f_ftype = ftype; of->f_fid = tree_fid; /* of->f_persistid see smb2_create */ - of->f_uniqid = uniqid; + of->f_uniqid = SMB_UNIQ_FID(); of->f_opened_by_pid = sr->smb_pid; of->f_granted_access = op->desired_access; of->f_share_access = op->share_access; of->f_create_options = op->create_options; - of->f_cr = (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) ? - smb_user_getprivcred(sr->uid_user) : sr->uid_user->u_cred; - crhold(of->f_cr); - of->f_server = tree->t_server; - of->f_session = tree->t_session; + if (user != NULL) { + if ((op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) != 0) + of->f_cr = smb_user_getprivcred(user); + else + of->f_cr = user->u_cred; + crhold(of->f_cr); + } + of->f_server = sr->sr_server; + of->f_session = sr->session; /* may be NULL */ + (void) memset(of->f_lock_seq, -1, SMB_OFILE_LSEQ_MAX); of->f_mode = smb_fsop_amask_to_omode(of->f_granted_access); @@ -361,11 +374,15 @@ smb_ofile_alloc( * held by our caller, until smb_ofile_open puts this * ofile on the node ofile list with smb_node_add_ofile. */ - smb_user_hold_internal(sr->uid_user); - smb_tree_hold_internal(tree); - of->f_user = sr->uid_user; - of->f_tree = tree; - of->f_node = node; + if (user != NULL) { + smb_user_hold_internal(user); + of->f_user = user; + } + if (tree != NULL) { + smb_tree_hold_internal(tree); + of->f_tree = tree; + } + of->f_node = node; /* may be NULL */ return (of); } @@ -448,6 +465,9 @@ smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec) return; } + /* + * Only one thread here (the one that that set f_state closing) + */ switch (of->f_ftype) { case SMB_FTYPE_BYTE_PIPE: case SMB_FTYPE_MESG_PIPE: @@ -456,6 +476,8 @@ smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec) break; case SMB_FTYPE_DISK: + if (of->dh_persist) + smb2_dh_close_persistent(of); if (of->f_persistid != 0) smb_ofile_del_persistid(of); if (of->f_lease != NULL) @@ -961,6 +983,9 @@ smb_ofile_lookup_by_persistid(smb_request_t *sr, uint64_t persistid) smb_ofile_t *of; uint_t idx; + if (persistid == 0) + return (NULL); + hash = sr->sr_server->sv_persistid_ht; idx = smb_hash_uint64(hash, persistid); bucket = &hash->buckets[idx]; @@ -981,28 +1006,132 @@ smb_ofile_lookup_by_persistid(smb_request_t *sr, uint64_t persistid) } /* - * Create a (unique) persistent ID for a new ofile, - * and add this ofile to the persistid hash table. + * Create a (unique) durable/persistent ID for a new ofile, + * and add this ofile to the persistid hash table. This ID + * is referred to as the persistent ID in the protocol spec, + * so that's what we call it too, though the persistence may + * vary. "Durable" handles are persistent across reconnects + * but not server reboots. Persistent handles are persistent + * across server reboots too. + * + * Note that persistent IDs need to be unique for the lifetime of + * any given ofile. For normal (non-persistent) ofiles we can just + * use a persistent ID derived from the ofile memory address, as + * these don't ever live beyond the current OS boot lifetime. + * + * Persistent handles are re-imported after server restart, and + * generally have a different memory address after import than + * they had in the previous OS boot lifetime, so for these we + * use a randomly assigned value that won't conflict with any + * non-persistent (durable) handles. Ensuring that a randomly + * generated ID is unique requires a search of the ofiles in one + * hash bucket, which we'd rather avoid for non-persistent opens. + * + * The solution used here is to divide the persistent ID space + * in half (odd and even values) where durable opens use an ID + * derived from the ofile address (which is always even), and + * persistent opens use an ID generated randomly (always odd). + * + * smb_ofile_set_persistid_dh() sets a durable handle ID and + * smb_ofile_set_persistid_ph() sets a persistent handle ID. */ void -smb_ofile_set_persistid(smb_ofile_t *of) +smb_ofile_set_persistid_dh(smb_ofile_t *of) { smb_hash_t *hash = of->f_server->sv_persistid_ht; smb_bucket_t *bucket; smb_llist_t *ll; + uint64_t persistid; uint_t idx; - of->f_persistid = SMB_OFILE_PERSISTID(of); + persistid = (uintptr_t)of; + /* Avoid showing object addresses */ + persistid ^= ((uintptr_t)&smb_cache_ofile); + /* make sure it's even */ + persistid &= ~((uint64_t)1); - idx = smb_hash_uint64(hash, of->f_persistid); + idx = smb_hash_uint64(hash, persistid); bucket = &hash->buckets[idx]; ll = &bucket->b_list; smb_llist_enter(ll, RW_WRITER); - smb_llist_insert_tail(ll, of); + if (of->f_persistid == 0) { + of->f_persistid = persistid; + smb_llist_insert_tail(ll, of); + } smb_llist_exit(ll); } void +smb_ofile_set_persistid_ph(smb_ofile_t *of) +{ + uint64_t persistid; + int rc; + +top: + (void) random_get_pseudo_bytes((uint8_t *)&persistid, + sizeof (persistid)); + if (persistid == 0) { + cmn_err(CE_NOTE, "random gave all zeros!"); + goto top; + } + /* make sure it's odd */ + persistid |= (uint64_t)1; + + /* + * Try inserting with this persistent ID. + */ + rc = smb_ofile_insert_persistid(of, persistid); + if (rc == EEXIST) + goto top; + if (rc != 0) { + cmn_err(CE_NOTE, "set persistid rc=%d", rc); + } +} + +/* + * Insert an ofile into the persistid hash table. + * If the persistent ID is in use, error. + */ +int +smb_ofile_insert_persistid(smb_ofile_t *new_of, uint64_t persistid) +{ + smb_hash_t *hash = new_of->f_server->sv_persistid_ht; + smb_bucket_t *bucket; + smb_llist_t *ll; + smb_ofile_t *of; + uint_t idx; + + ASSERT(persistid != 0); + + /* + * Look to see if this key alreay exists. + */ + idx = smb_hash_uint64(hash, persistid); + bucket = &hash->buckets[idx]; + ll = &bucket->b_list; + + smb_llist_enter(ll, RW_WRITER); + of = smb_llist_head(ll); + while (of != NULL) { + if (of->f_persistid == persistid) { + /* already in use */ + smb_llist_exit(ll); + return (EEXIST); + } + of = smb_llist_next(ll, of); + } + + /* Not found, so OK to insert. */ + if (new_of->f_persistid == 0) { + new_of->f_persistid = persistid; + smb_llist_insert_tail(ll, new_of); + } + smb_llist_exit(ll); + + return (0); +} + +void smb_ofile_del_persistid(smb_ofile_t *of) { smb_hash_t *hash = of->f_server->sv_persistid_ht; @@ -1014,7 +1143,10 @@ smb_ofile_del_persistid(smb_ofile_t *of) bucket = &hash->buckets[idx]; ll = &bucket->b_list; smb_llist_enter(ll, RW_WRITER); - smb_llist_remove(ll, of); + if (of->f_persistid != 0) { + smb_llist_remove(ll, of); + of->f_persistid = 0; + } smb_llist_exit(ll); } @@ -1390,6 +1522,7 @@ smb_ofile_free(smb_ofile_t *of) of->f_magic = (uint32_t)~SMB_OFILE_MAGIC; list_destroy(&of->f_notify.nc_waiters); + mutex_destroy(&of->dh_nvlock); mutex_destroy(&of->f_mutex); kmem_cache_free(smb_cache_ofile, of); } diff --git a/usr/src/uts/common/fs/smbsrv/smb_pathname.c b/usr/src/uts/common/fs/smbsrv/smb_pathname.c index a8f5ae3aa4..fbf003c7c0 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 2016 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #include <smbsrv/smb_kproto.h> @@ -154,7 +154,7 @@ smb_pathname_reduce( pathname_t ppn; char *usepath; int lookup_flags = FOLLOW; - int trailing_slash = 0; + int trailing_slash = 0; int err = 0; int len; smb_node_t *vss_cur_node; @@ -423,6 +423,10 @@ smb_pathname(smb_request_t *sr, char *path, int flags, if ((err = pn_set(&pn, namep)) != 0) break; + /* We want the DOS attributes. */ + bzero(&attr, sizeof (attr)); + attr.sa_mask = SMB_AT_DOSATTR; + local_flags = flags & FIGNORECASE; err = smb_pathname_lookup(&pn, &rpn, local_flags, &vp, rootvp, dnode->vp, &attr, cred); @@ -1066,6 +1070,27 @@ smb_is_stream_name(char *path) } /* + * Is this stream node a "restricted" type? + */ +boolean_t +smb_strname_restricted(char *strname) +{ + char *stype; + + stype = strrchr(strname, ':'); + if (stype == NULL) + return (B_FALSE); + + /* + * Only ":$CA" is restricted (for now). + */ + if (strcmp(stype, ":$CA") == 0) + return (B_TRUE); + + return (B_FALSE); +} + +/* * smb_validate_stream_name * * B_FALSE will be returned, and the error status ser in the sr, if: @@ -1079,6 +1104,7 @@ boolean_t smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn) { static char *strmtype[] = { + "$CA", "$DATA", "$INDEX_ALLOCATION" }; diff --git a/usr/src/uts/common/fs/smbsrv/smb_server.c b/usr/src/uts/common/fs/smbsrv/smb_server.c index 0291cacc1c..6fc64e0fac 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_server.c +++ b/usr/src/uts/common/fs/smbsrv/smb_server.c @@ -20,8 +20,8 @@ */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2017 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2017 by Delphix. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -229,12 +229,12 @@ static void smb_server_fsop_stop(smb_server_t *); static void smb_event_cancel(smb_server_t *, uint32_t); static uint32_t smb_event_alloc_txid(void); -static void smb_server_disconnect_share(smb_llist_t *, const char *); -static void smb_server_enum_users(smb_llist_t *, smb_svcenum_t *); -static void smb_server_enum_trees(smb_llist_t *, smb_svcenum_t *); -static int smb_server_session_disconnect(smb_llist_t *, const char *, +static void smb_server_disconnect_share(smb_server_t *, const char *); +static void smb_server_enum_users(smb_server_t *, smb_svcenum_t *); +static void smb_server_enum_trees(smb_server_t *, smb_svcenum_t *); +static int smb_server_session_disconnect(smb_server_t *, const char *, const char *); -static int smb_server_fclose(smb_llist_t *, uint32_t); +static int smb_server_fclose(smb_server_t *, uint32_t); static int smb_server_kstat_update(kstat_t *, int); static int smb_server_legacy_kstat_update(kstat_t *, int); static void smb_server_listener_init(smb_server_t *, smb_listener_daemon_t *, @@ -473,14 +473,8 @@ smb_server_create(void) * activity associated that server has ceased before destroying it. */ int -smb_server_delete(void) +smb_server_delete(smb_server_t *sv) { - smb_server_t *sv; - int rc; - - rc = smb_server_lookup(&sv); - if (rc != 0) - return (rc); mutex_enter(&sv->sv_mutex); switch (sv->sv_state) { @@ -608,6 +602,7 @@ smb_server_start(smb_ioc_start_t *ioc) int rc = 0; int family; smb_server_t *sv; + cred_t *ucr; rc = smb_server_lookup(&sv); if (rc) @@ -620,6 +615,31 @@ smb_server_start(smb_ioc_start_t *ioc) if ((rc = smb_server_fsop_start(sv)) != 0) break; + /* + * Note: smb_kshare_start needs sv_session. + */ + sv->sv_session = smb_session_create(NULL, 0, sv, 0); + if (sv->sv_session == NULL) { + rc = ENOMEM; + break; + } + + /* + * Create a logon on the server session, + * used when importing CA shares. + */ + sv->sv_rootuser = smb_user_new(sv->sv_session); + ucr = smb_kcred_create(); + rc = smb_user_logon(sv->sv_rootuser, ucr, "", "root", + SMB_USER_FLAG_ADMIN, 0, 0); + crfree(ucr); + ucr = NULL; + if (rc != 0) { + cmn_err(CE_NOTE, "smb_server_start: " + "failed to create root user"); + break; + } + if ((rc = smb_kshare_start(sv)) != 0) break; @@ -637,9 +657,8 @@ smb_server_start(smb_ioc_start_t *ioc) sv->sv_cfg.skc_maxconnections, INT_MAX, curzone->zone_zsched, TASKQ_DYNAMIC); - sv->sv_session = smb_session_create(NULL, 0, sv, 0); - - if (sv->sv_worker_pool == NULL || sv->sv_session == NULL) { + if (sv->sv_worker_pool == NULL || + sv->sv_receiver_pool == NULL) { rc = ENOMEM; break; } @@ -888,11 +907,11 @@ smb_server_enum(smb_ioc_svcenum_t *ioc) switch (svcenum->se_type) { case SMB_SVCENUM_TYPE_USER: - smb_server_enum_users(&sv->sv_session_list, svcenum); + smb_server_enum_users(sv, svcenum); break; case SMB_SVCENUM_TYPE_TREE: case SMB_SVCENUM_TYPE_FILE: - smb_server_enum_trees(&sv->sv_session_list, svcenum); + smb_server_enum_trees(sv, svcenum); break; default: rc = EINVAL; @@ -908,7 +927,6 @@ smb_server_enum(smb_ioc_svcenum_t *ioc) int smb_server_session_close(smb_ioc_session_t *ioc) { - smb_llist_t *ll; smb_server_t *sv; int cnt; int rc; @@ -916,8 +934,7 @@ smb_server_session_close(smb_ioc_session_t *ioc) if ((rc = smb_server_lookup(&sv)) != 0) return (rc); - ll = &sv->sv_session_list; - cnt = smb_server_session_disconnect(ll, ioc->client, ioc->username); + cnt = smb_server_session_disconnect(sv, ioc->client, ioc->username); smb_server_release(sv); @@ -933,15 +950,13 @@ int smb_server_file_close(smb_ioc_fileid_t *ioc) { uint32_t uniqid = ioc->uniqid; - smb_llist_t *ll; smb_server_t *sv; int rc; if ((rc = smb_server_lookup(&sv)) != 0) return (rc); - ll = &sv->sv_session_list; - rc = smb_server_fclose(ll, uniqid); + rc = smb_server_fclose(sv, uniqid); smb_server_release(sv); return (rc); @@ -962,17 +977,16 @@ smb_server_get_session_count(smb_server_t *sv) } /* - * Gets the vnode of the specified share path. - * - * A hold on the returned vnode pointer is taken so the caller - * must call VN_RELE. + * Gets the smb_node of the specified share path. + * Node is returned held (caller must rele.) */ int -smb_server_sharevp(smb_server_t *sv, const char *shr_path, vnode_t **vp) +smb_server_share_lookup(smb_server_t *sv, const char *shr_path, + smb_node_t **nodepp) { smb_request_t *sr; smb_node_t *fnode = NULL; - smb_node_t *dnode; + smb_node_t *dnode = NULL; char last_comp[MAXNAMELEN]; int rc = 0; @@ -1009,10 +1023,7 @@ smb_server_sharevp(smb_server_t *sv, const char *shr_path, vnode_t **vp) ASSERT(fnode->vp && fnode->vp->v_vfsp); - VN_HOLD(fnode->vp); - *vp = fnode->vp; - - smb_node_release(fnode); + *nodepp = fnode; return (0); } @@ -1054,7 +1065,6 @@ int smb_server_unshare(const char *sharename) { smb_server_t *sv; - smb_llist_t *ll; int rc; if ((rc = smb_server_lookup(&sv))) @@ -1072,8 +1082,7 @@ smb_server_unshare(const char *sharename) } mutex_exit(&sv->sv_mutex); - ll = &sv->sv_session_list; - smb_server_disconnect_share(ll, sharename); + smb_server_disconnect_share(sv, sharename); smb_server_release(sv); return (0); @@ -1084,10 +1093,12 @@ smb_server_unshare(const char *sharename) * Typically called when a share has been removed. */ static void -smb_server_disconnect_share(smb_llist_t *ll, const char *sharename) +smb_server_disconnect_share(smb_server_t *sv, const char *sharename) { + smb_llist_t *ll; smb_session_t *session; + ll = &sv->sv_session_list; smb_llist_enter(ll, RW_READER); session = smb_llist_head(ll); @@ -1498,9 +1509,17 @@ smb_server_shutdown(smb_server_t *sv) * normal sessions, this happens in smb_session_cancel, * but that's not called for the server session. */ + if (sv->sv_rootuser != NULL) { + smb_user_logoff(sv->sv_rootuser); + smb_user_release(sv->sv_rootuser); + sv->sv_rootuser = NULL; + } if (sv->sv_session != NULL) { smb_slist_wait_for_empty(&sv->sv_session->s_req_list); + /* Just in case import left users and trees */ + smb_session_logoff(sv->sv_session); + smb_session_delete(sv->sv_session); sv->sv_session = NULL; } @@ -1801,8 +1820,9 @@ smb_server_release(smb_server_t *sv) * Enumerate the users associated with a session list. */ static void -smb_server_enum_users(smb_llist_t *ll, smb_svcenum_t *svcenum) +smb_server_enum_users(smb_server_t *sv, smb_svcenum_t *svcenum) { + smb_llist_t *ll = &sv->sv_session_list; smb_session_t *sn; smb_llist_t *ulist; smb_user_t *user; @@ -1843,8 +1863,9 @@ smb_server_enum_users(smb_llist_t *ll, smb_svcenum_t *svcenum) * Enumerate the trees/files associated with a session list. */ static void -smb_server_enum_trees(smb_llist_t *ll, smb_svcenum_t *svcenum) +smb_server_enum_trees(smb_server_t *sv, smb_svcenum_t *svcenum) { + smb_llist_t *ll = &sv->sv_session_list; smb_session_t *sn; smb_llist_t *tlist; smb_tree_t *tree; @@ -1886,9 +1907,10 @@ smb_server_enum_trees(smb_llist_t *ll, smb_svcenum_t *svcenum) * Empty strings are treated as wildcards. */ static int -smb_server_session_disconnect(smb_llist_t *ll, +smb_server_session_disconnect(smb_server_t *sv, const char *client, const char *name) { + smb_llist_t *ll = &sv->sv_session_list; smb_session_t *sn; smb_llist_t *ulist; smb_user_t *user; @@ -1933,13 +1955,15 @@ smb_server_session_disconnect(smb_llist_t *ll, * Close a file by its unique id. */ static int -smb_server_fclose(smb_llist_t *ll, uint32_t uniqid) +smb_server_fclose(smb_server_t *sv, uint32_t uniqid) { + smb_llist_t *ll; smb_session_t *sn; smb_llist_t *tlist; smb_tree_t *tree; int rc = ENOENT; + ll = &sv->sv_session_list; smb_llist_enter(ll, RW_READER); sn = smb_llist_head(ll); diff --git a/usr/src/uts/common/fs/smbsrv/smb_session.c b/usr/src/uts/common/fs/smbsrv/smb_session.c index 205c21179b..2878df28e7 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 2018 Nexenta Systems, Inc. All rights reserved. + * Copyright 2019 Nexenta Systems, Inc. All rights reserved. */ #include <sys/atomic.h> @@ -72,8 +72,6 @@ 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_session_disconnect_trees(smb_session_t *); static void smb_request_init_command_mbuf(smb_request_t *sr); static void smb_session_genkey(smb_session_t *); @@ -752,7 +750,22 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv, smb_rwx_init(&session->s_lock); - if (new_so != NULL) { + session->s_srqueue = &sv->sv_srqueue; + smb_server_get_cfg(sv, &session->s_cfg); + + if (new_so == NULL) { + /* + * This call is creating the special "server" session, + * used for kshare export, oplock breaks, CA import. + * CA import creates temporary trees on this session + * and those should never get map/unmap up-calls, so + * force the map/unmap flags zero on this session. + * Set a "modern" dialect for CA import too, so + * pathname parse doesn't do OS/2 stuff, etc. + */ + session->s_cfg.skc_execflags = 0; + session->dialect = session->s_cfg.skc_max_protocol; + } else { if (family == AF_INET) { slen = sizeof (sin); (void) ksocket_getsockname(new_so, @@ -794,8 +807,6 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv, else smb_server_inc_tcp_sess(sv); } - smb_server_get_cfg(sv, &session->s_cfg); - session->s_srqueue = &sv->sv_srqueue; /* * The initial new request handler is special, @@ -1006,117 +1017,35 @@ smb_session_lookup_tree( } /* - * Find the first connected tree that matches the specified sharename. - * If the specified tree is NULL the search starts from the beginning of - * the user's tree list. If a tree is provided the search starts just - * after that tree. - */ -smb_tree_t * -smb_session_lookup_share( - smb_session_t *session, - const char *sharename, - smb_tree_t *tree) -{ - SMB_SESSION_VALID(session); - ASSERT(sharename); - - smb_llist_enter(&session->s_tree_list, RW_READER); - - if (tree) { - ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC); - ASSERT(tree->t_session == session); - tree = smb_llist_next(&session->s_tree_list, tree); - } else { - tree = smb_llist_head(&session->s_tree_list); - } - - while (tree) { - ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC); - ASSERT(tree->t_session == session); - if (smb_strcasecmp(tree->t_sharename, sharename, 0) == 0) { - if (smb_tree_hold(tree)) { - smb_llist_exit(&session->s_tree_list); - return (tree); - } - } - tree = smb_llist_next(&session->s_tree_list, tree); - } - - smb_llist_exit(&session->s_tree_list); - return (NULL); -} - -/* - * Find the first connected tree that matches the specified volume name. - * If the specified tree is NULL the search starts from the beginning of - * the user's tree list. If a tree is provided the search starts just - * after that tree. - */ -smb_tree_t * -smb_session_lookup_volume( - smb_session_t *session, - const char *name, - smb_tree_t *tree) -{ - SMB_SESSION_VALID(session); - ASSERT(name); - - smb_llist_enter(&session->s_tree_list, RW_READER); - - if (tree) { - ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC); - ASSERT(tree->t_session == session); - tree = smb_llist_next(&session->s_tree_list, tree); - } else { - tree = smb_llist_head(&session->s_tree_list); - } - - while (tree) { - ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC); - ASSERT(tree->t_session == session); - - if (smb_strcasecmp(tree->t_volume, name, 0) == 0) { - if (smb_tree_hold(tree)) { - smb_llist_exit(&session->s_tree_list); - return (tree); - } - } - - tree = smb_llist_next(&session->s_tree_list, tree); - } - - smb_llist_exit(&session->s_tree_list); - return (NULL); -} - -/* * Disconnect all trees that match the specified client process-id. + * Used by the SMB1 "process exit" request. */ void smb_session_close_pid( smb_session_t *session, uint32_t pid) { + smb_llist_t *tree_list = &session->s_tree_list; smb_tree_t *tree; - SMB_SESSION_VALID(session); + smb_llist_enter(tree_list, RW_READER); - tree = smb_session_get_tree(session, NULL); + tree = smb_llist_head(tree_list); while (tree) { - smb_tree_t *next; - ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC); - ASSERT(tree->t_session == session); - smb_tree_close_pid(tree, pid); - next = smb_session_get_tree(session, tree); - smb_tree_release(tree); - tree = next; + if (smb_tree_hold(tree)) { + smb_tree_close_pid(tree, pid); + smb_tree_release(tree); + } + tree = smb_llist_next(tree_list, tree); } + + smb_llist_exit(tree_list); } static void -smb_session_tree_dtor(void *t) +smb_session_tree_dtor(void *arg) { - smb_tree_t *tree = (smb_tree_t *)t; + smb_tree_t *tree = arg; smb_tree_disconnect(tree, B_TRUE); /* release the ref acquired during the traversal loop */ @@ -1167,84 +1096,76 @@ static void smb_session_disconnect_trees( smb_session_t *session) { - smb_tree_t *tree, *next_tree; + smb_llist_t *tree_list = &session->s_tree_list; + smb_tree_t *tree; - SMB_SESSION_VALID(session); + smb_llist_enter(tree_list, RW_READER); - tree = smb_session_get_tree(session, NULL); + tree = smb_llist_head(tree_list); while (tree) { - ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC); - ASSERT(tree->t_session == session); - smb_tree_disconnect(tree, B_TRUE); - next_tree = smb_session_get_tree(session, tree); - smb_tree_release(tree); - tree = next_tree; + if (smb_tree_hold(tree)) { + smb_llist_post(tree_list, tree, + smb_session_tree_dtor); + } + tree = smb_llist_next(tree_list, tree); } + + /* drop the lock and flush the dtor queue */ + smb_llist_exit(tree_list); } /* - * Disconnect all trees that match the specified share name. + * Variant of smb_session_tree_dtor that also + * cancels requests using this tree. */ -void -smb_session_disconnect_share( - smb_session_t *session, - const char *sharename) +static void +smb_session_tree_kill(void *arg) { - smb_tree_t *tree; - smb_tree_t *next; + smb_tree_t *tree = arg; - SMB_SESSION_VALID(session); + SMB_TREE_VALID(tree); - tree = smb_session_lookup_share(session, sharename, NULL); - while (tree) { - ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC); - ASSERT(tree->t_session == session); - smb_tree_disconnect(tree, B_TRUE); - smb_session_cancel_requests(session, tree, NULL); - next = smb_session_lookup_share(session, sharename, tree); - smb_tree_release(tree); - tree = next; - } + smb_tree_disconnect(tree, B_TRUE); + smb_session_cancel_requests(tree->t_session, tree, NULL); + + /* release the ref acquired during the traversal loop */ + smb_tree_release(tree); } /* - * Get the next connected tree in the list. A reference is taken on - * the tree, which can be released later with smb_tree_release(). - * - * If the specified tree is NULL the search starts from the beginning of - * the tree list. If a tree is provided the search starts just after - * that tree. - * - * Returns NULL if there are no connected trees in the list. + * Disconnect all trees that match the specified share name, + * and kill requests using those trees. */ -static smb_tree_t * -smb_session_get_tree( +void +smb_session_disconnect_share( smb_session_t *session, - smb_tree_t *tree) + const char *sharename) { - smb_llist_t *tree_list; + smb_llist_t *ll; + smb_tree_t *tree; SMB_SESSION_VALID(session); - tree_list = &session->s_tree_list; - smb_llist_enter(tree_list, RW_READER); + ll = &session->s_tree_list; + smb_llist_enter(ll, RW_READER); - if (tree) { - ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC); - tree = smb_llist_next(tree_list, tree); - } else { - tree = smb_llist_head(tree_list); - } + for (tree = smb_llist_head(ll); + tree != NULL; + tree = smb_llist_next(ll, tree)) { - while (tree) { - if (smb_tree_hold(tree)) - break; + SMB_TREE_VALID(tree); + ASSERT(tree->t_session == session); - tree = smb_llist_next(tree_list, tree); + if (smb_strcasecmp(tree->t_sharename, sharename, 0) != 0) + continue; + + if (smb_tree_hold(tree)) { + smb_llist_post(ll, tree, + smb_session_tree_kill); + } } - smb_llist_exit(tree_list); - return (tree); + smb_llist_exit(ll); } /* @@ -1255,7 +1176,7 @@ smb_session_get_tree( * disconnect (SMB_SESSION_STATE_DISCONNECTED). * If client-initiated, save durable handles. */ -static void +void smb_session_logoff(smb_session_t *session) { smb_llist_t *ulist; @@ -1279,9 +1200,6 @@ top: // smb_user_hold_internal(user); user->u_refcnt++; mutex_exit(&user->u_mutex); - if (user->u_session->s_state == - SMB_SESSION_STATE_DISCONNECTED) - user->preserve_opens = SMB2_DH_PRESERVE_ALL; smb_user_logoff(user); smb_user_release(user); break; diff --git a/usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c b/usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c index 86ce24c0b0..7c4be2f56e 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c +++ b/usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c @@ -346,6 +346,11 @@ smb_oplock_async_break(void *arg) break; } + if (sr->dh_nvl_dirty) { + sr->dh_nvl_dirty = B_FALSE; + smb2_dh_update_nvfile(sr); + } + sr->sr_state = SMB_REQ_STATE_COMPLETED; smb_request_free(sr); } @@ -444,6 +449,10 @@ smb_oplock_send_brk(smb_request_t *sr) if (lease != NULL) lease->ls_state = NewLevel & CACHE_RWH; ofile->f_oplock.og_state = NewLevel; + + if (ofile->dh_persist) { + smb2_dh_update_oplock(sr, ofile); + } } /* @@ -583,6 +592,10 @@ smb_oplock_send_brk(smb_request_t *sr) if (lease != NULL) { lease->ls_state = NewLevel & CACHE_RWH; } + + if (ofile->dh_persist) { + smb2_dh_update_oplock(sr, ofile); + } } /* diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree.c b/usr/src/uts/common/fs/smbsrv/smb_tree.c index 5020dec794..aedacf2123 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_tree.c +++ b/usr/src/uts/common/fs/smbsrv/smb_tree.c @@ -184,8 +184,6 @@ 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 void smb_tree_dealloc(void *); static boolean_t smb_tree_is_connected_locked(smb_tree_t *); static char *smb_tree_get_sharename(char *); @@ -193,9 +191,7 @@ 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 *); static void smb_tree_log(smb_request_t *, const char *, const char *, ...); -static void smb_tree_close_odirs(smb_tree_t *, uint16_t); -static smb_ofile_t *smb_tree_get_ofile(smb_tree_t *, smb_ofile_t *); -static smb_odir_t *smb_tree_get_odir(smb_tree_t *, smb_odir_t *); +static void smb_tree_close_odirs(smb_tree_t *, uint32_t); static void smb_tree_set_execinfo(smb_tree_t *, smb_shr_execinfo_t *, int); static int smb_tree_enum_private(smb_tree_t *, smb_svcenum_t *); static int smb_tree_netinfo_encode(smb_tree_t *, uint8_t *, size_t, uint32_t *); @@ -303,10 +299,13 @@ out: /* * Disconnect a tree. + * + * The "do_exec" arg is obsolete and ignored. */ void smb_tree_disconnect(smb_tree_t *tree, boolean_t do_exec) { + _NOTE(ARGUNUSED(do_exec)) smb_shr_execinfo_t execinfo; ASSERT(tree->t_magic == SMB_TREE_MAGIC); @@ -314,34 +313,27 @@ smb_tree_disconnect(smb_tree_t *tree, boolean_t do_exec) mutex_enter(&tree->t_mutex); ASSERT(tree->t_refcnt); - if (smb_tree_is_connected_locked(tree)) { - /* - * Indicate that the disconnect process has started. - */ - tree->t_state = SMB_TREE_STATE_DISCONNECTING; + if (!smb_tree_is_connected_locked(tree)) { mutex_exit(&tree->t_mutex); - - if (do_exec) { - /* - * The files opened under this tree are closed. - */ - smb_ofile_close_all(tree, 0); - /* - * The directories opened under this tree are closed. - */ - smb_tree_close_odirs(tree, 0); - } - - mutex_enter(&tree->t_mutex); - tree->t_state = SMB_TREE_STATE_DISCONNECTED; - smb_server_dec_trees(tree->t_server); + return; } + /* + * Indicate that the disconnect process has started. + */ + tree->t_state = SMB_TREE_STATE_DISCONNECTING; mutex_exit(&tree->t_mutex); - if (do_exec && (tree->t_state == SMB_TREE_STATE_DISCONNECTED) && - (tree->t_execflags & SMB_EXEC_UNMAP)) { + /* + * The files opened under this tree are closed. + */ + smb_ofile_close_all(tree, 0); + /* + * The directories opened under this tree are closed. + */ + smb_tree_close_odirs(tree, 0); + if ((tree->t_execflags & SMB_EXEC_UNMAP) != 0) { smb_tree_set_execinfo(tree, &execinfo, SMB_EXEC_UNMAP); (void) smb_kshare_exec(tree->t_server, &execinfo); } @@ -408,7 +400,7 @@ smb_tree_release( tree->t_refcnt--; switch (tree->t_state) { - case SMB_TREE_STATE_DISCONNECTED: + case SMB_TREE_STATE_DISCONNECTING: if (tree->t_refcnt == 0) { smb_session_t *ssn = tree->t_session; tree->t_state = SMB_TREE_STATE_DISCONNECTED; @@ -417,7 +409,6 @@ smb_tree_release( } break; case SMB_TREE_STATE_CONNECTED: - case SMB_TREE_STATE_DISCONNECTING: break; default: ASSERT(0); @@ -463,31 +454,29 @@ smb_tree_has_feature(smb_tree_t *tree, uint32_t flags) int smb_tree_enum(smb_tree_t *tree, smb_svcenum_t *svcenum) { + smb_llist_t *of_list; smb_ofile_t *of; - smb_ofile_t *next; int rc = 0; - ASSERT(tree); - ASSERT(tree->t_magic == SMB_TREE_MAGIC); - if (svcenum->se_type == SMB_SVCENUM_TYPE_TREE) return (smb_tree_enum_private(tree, svcenum)); - of = smb_tree_get_ofile(tree, NULL); - while (of) { - ASSERT(of->f_tree == tree); + of_list = &tree->t_ofile_list; + smb_llist_enter(of_list, RW_READER); - rc = smb_ofile_enum(of, svcenum); - if (rc != 0) { + of = smb_llist_head(of_list); + while (of) { + if (smb_ofile_hold(of)) { + rc = smb_ofile_enum(of, svcenum); smb_ofile_release(of); - break; } - - next = smb_tree_get_ofile(tree, of); - smb_ofile_release(of); - of = next; + if (rc != 0) + break; + of = smb_llist_next(of_list, of); } + smb_llist_exit(of_list); + return (rc); } @@ -662,6 +651,9 @@ smb_tree_chkaccess(smb_request_t *sr, smb_kshare_t *shr, vnode_t *vp) return (access); } +/* How long should tree connect wait for DH import to complete? */ +int smb_tcon_import_wait = 20; /* sec. */ + /* * Connect a share for use with files and directories. */ @@ -671,16 +663,14 @@ 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 = tcon->si; char *service = tcon->service; - char last_component[MAXNAMELEN]; smb_tree_t *tree; - cred_t *kcr; int rc; uint32_t access; smb_shr_execinfo_t execinfo; + clock_t time; ASSERT(user); ASSERT(user->u_cred); @@ -694,34 +684,34 @@ smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon) /* * Check that the shared directory exists. - * Client might not have access to the path _leading_ to the share, - * so we use "kcred" to get to the share root. */ - kcr = zone_kcred(); - rc = smb_pathname_reduce(sr, kcr, si->shr_path, 0, 0, &dnode, - last_component); - if (rc == 0) { - rc = smb_fsop_lookup(sr, kcr, SMB_FOLLOW_LINKS, - sr->sr_server->si_root_smb_node, dnode, last_component, - &snode); - - smb_node_release(dnode); - } - - if (rc) { - if (snode) - smb_node_release(snode); - + snode = si->shr_root_node; + if (snode == NULL) { smb_tree_log(sr, sharename, "bad path: %s", si->shr_path); return (NT_STATUS_BAD_NETWORK_NAME); } if ((access = smb_tree_chkaccess(sr, si, snode->vp)) == 0) { - smb_node_release(snode); return (NT_STATUS_ACCESS_DENIED); } /* + * Wait for DH import of persistent handles to finish. + * If we timeout, it's not clear what status to return, + * but as the share is not really available yet, let's + * return the status for "no such share". + */ + time = SEC_TO_TICK(smb_tcon_import_wait) + ddi_get_lbolt(); + mutex_enter(&si->shr_mutex); + while (si->shr_import_busy != NULL) { + if (cv_timedwait(&si->shr_cv, &si->shr_mutex, time) < 0) { + mutex_exit(&si->shr_mutex); + return (NT_STATUS_BAD_NETWORK_NAME); + } + } + mutex_exit(&si->shr_mutex); + + /* * Set up the OptionalSupport for this share. */ tcon->optional_support = SMB_SUPPORT_SEARCH_BITS; @@ -758,8 +748,6 @@ smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon) tree = smb_tree_alloc(sr, si, snode, access, sr->sr_cfg->skc_execflags); - smb_node_release(snode); - if (tree == NULL) return (NT_STATUS_INSUFF_SERVER_RESOURCES); @@ -769,7 +757,17 @@ smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon) rc = smb_kshare_exec(tree->t_server, &execinfo); if ((rc != 0) && (tree->t_execflags & SMB_EXEC_TERM)) { - smb_tree_disconnect(tree, B_FALSE); + /* + * Inline parts of: smb_tree_disconnect() + * Not using smb_tree_disconnect() for cleanup + * here because: we don't want an exec up-call, + * and there can't be any opens as we never + * returned this TID to the client. + */ + mutex_enter(&tree->t_mutex); + tree->t_state = SMB_TREE_STATE_DISCONNECTING; + mutex_exit(&tree->t_mutex); + smb_tree_release(tree); return (NT_STATUS_ACCESS_DENIED); } @@ -901,7 +899,7 @@ smb_tree_connect_ipc(smb_request_t *sr, smb_arg_tcon_t *tcon) /* * Allocate a tree. */ -static smb_tree_t * +smb_tree_t * smb_tree_alloc(smb_request_t *sr, const smb_kshare_t *si, smb_node_t *snode, uint32_t access, uint32_t execflags) { @@ -1001,6 +999,8 @@ smb_tree_dealloc(void *arg) ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED); ASSERT(tree->t_refcnt == 0); + smb_server_dec_trees(tree->t_server); + session = tree->t_session; smb_llist_enter(&session->s_tree_list, RW_WRITER); smb_llist_remove(&session->s_tree_list, tree); @@ -1199,6 +1199,9 @@ smb_tree_get_flags(const smb_kshare_t *si, vfs_t *vfsp, smb_tree_t *tree) if (si->shr_flags & SMB_SHRF_ABE) flags |= SMB_TREE_ABE; + if (si->shr_flags & SMB_SHRF_CA) + flags |= SMB_TREE_CA; + if (si->shr_flags & SMB_SHRF_FSO) flags |= SMB_TREE_FORCE_L2_OPLOCK; @@ -1361,83 +1364,6 @@ smb_tree_is_connected(smb_tree_t *tree) } /* - * Get the next open ofile in the list. A reference is taken on - * the ofile, which can be released later with smb_ofile_release(). - * - * If the specified ofile is NULL, search from the beginning of the - * list. Otherwise, the search starts just after that ofile. - * - * Returns NULL if there are no open files in the list. - */ -static smb_ofile_t * -smb_tree_get_ofile(smb_tree_t *tree, smb_ofile_t *of) -{ - smb_llist_t *ofile_list; - - ASSERT(tree); - ASSERT(tree->t_magic == SMB_TREE_MAGIC); - - ofile_list = &tree->t_ofile_list; - smb_llist_enter(ofile_list, RW_READER); - - if (of) { - ASSERT(of->f_magic == SMB_OFILE_MAGIC); - of = smb_llist_next(ofile_list, of); - } else { - of = smb_llist_head(ofile_list); - } - - while (of) { - if (smb_ofile_hold(of)) - break; - - of = smb_llist_next(ofile_list, of); - } - - smb_llist_exit(ofile_list); - return (of); -} - -/* - * smb_tree_get_odir - * - * Find the next odir in the tree's list of odirs, and obtain a - * hold on it. - * If the specified odir is NULL the search starts at the beginning - * of the tree's odir list, otherwise the search starts after the - * specified odir. - */ -static smb_odir_t * -smb_tree_get_odir(smb_tree_t *tree, smb_odir_t *od) -{ - smb_llist_t *od_list; - - ASSERT(tree); - ASSERT(tree->t_magic == SMB_TREE_MAGIC); - - od_list = &tree->t_odir_list; - smb_llist_enter(od_list, RW_READER); - - if (od) { - ASSERT(od->d_magic == SMB_ODIR_MAGIC); - od = smb_llist_next(od_list, od); - } else { - od = smb_llist_head(od_list); - } - - while (od) { - ASSERT(od->d_magic == SMB_ODIR_MAGIC); - - if (smb_odir_hold(od)) - break; - od = smb_llist_next(od_list, od); - } - - smb_llist_exit(od_list); - return (od); -} - -/* * smb_tree_close_odirs * * Close all open odirs in the tree's list which were opened by @@ -1445,25 +1371,34 @@ smb_tree_get_odir(smb_tree_t *tree, smb_odir_t *od) * If pid is zero, close all open odirs in the tree's list. */ static void -smb_tree_close_odirs(smb_tree_t *tree, uint16_t pid) +smb_tree_close_odirs(smb_tree_t *tree, uint32_t pid) { - smb_odir_t *od, *next_od; + smb_llist_t *od_list; + smb_odir_t *od; ASSERT(tree); ASSERT(tree->t_magic == SMB_TREE_MAGIC); - od = smb_tree_get_odir(tree, NULL); - while (od) { + od_list = &tree->t_odir_list; + smb_llist_enter(od_list, RW_READER); + + for (od = smb_llist_head(od_list); + od != NULL; + od = smb_llist_next(od_list, od)) { + ASSERT(od->d_magic == SMB_ODIR_MAGIC); ASSERT(od->d_tree == tree); - next_od = smb_tree_get_odir(tree, od); - if ((pid == 0) || (od->d_opened_by_pid == pid)) - smb_odir_close(od); - smb_odir_release(od); + if (pid != 0 && od->d_opened_by_pid != pid) + continue; - od = next_od; + if (smb_odir_hold(od)) { + smb_odir_close(od); + smb_odir_release(od); + } } + + smb_llist_exit(od_list); } static void diff --git a/usr/src/uts/common/fs/smbsrv/smb_user.c b/usr/src/uts/common/fs/smbsrv/smb_user.c index 0bfceb4ff4..74bb502c56 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_user.c +++ b/usr/src/uts/common/fs/smbsrv/smb_user.c @@ -303,7 +303,6 @@ smb_user_logon( * we always have an auth. socket to close. */ authsock = user->u_authsock; - ASSERT(authsock != NULL); user->u_authsock = NULL; tmo = user->u_auth_tmo; user->u_auth_tmo = NULL; @@ -325,7 +324,8 @@ smb_user_logon( (void) untimeout(tmo); /* This close can block, so not under the mutex. */ - smb_authsock_close(user, authsock); + if (authsock != NULL) + smb_authsock_close(user, authsock); return (0); } diff --git a/usr/src/uts/common/fs/smbsrv/smb_vfs.c b/usr/src/uts/common/fs/smbsrv/smb_vfs.c deleted file mode 100644 index ae631e4ffa..0000000000 --- a/usr/src/uts/common/fs/smbsrv/smb_vfs.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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. - */ - -#include <sys/vfs.h> -#include <smbsrv/smb_ktypes.h> -#include <smbsrv/smb_kproto.h> - -static smb_vfs_t *smb_vfs_find(smb_export_t *, vfs_t *); -static void smb_vfs_destroy(smb_vfs_t *); - -/* - * If a hold on the specified VFS has already been taken - * then only increment the reference count of the corresponding - * smb_vfs_t structure. If no smb_vfs_t structure has been created - * yet for the specified VFS then create one and take a hold on - * the VFS. - */ -int -smb_vfs_hold(smb_export_t *se, vfs_t *vfsp) -{ - smb_vfs_t *smb_vfs; - vnode_t *rootvp; - int rc; - - if (se == NULL || vfsp == NULL) - return (EINVAL); - - smb_llist_enter(&se->e_vfs_list, RW_WRITER); - - if ((smb_vfs = smb_vfs_find(se, vfsp)) != NULL) { - smb_vfs->sv_refcnt++; - DTRACE_PROBE1(smb_vfs_hold_hit, smb_vfs_t *, smb_vfs); - smb_llist_exit(&se->e_vfs_list); - return (0); - } - - if ((rc = VFS_ROOT(vfsp, &rootvp)) != 0) { - smb_llist_exit(&se->e_vfs_list); - return (rc); - } - - smb_vfs = kmem_cache_alloc(smb_kshare_cache_vfs, KM_SLEEP); - - bzero(smb_vfs, sizeof (smb_vfs_t)); - - smb_vfs->sv_magic = SMB_VFS_MAGIC; - smb_vfs->sv_refcnt = 1; - smb_vfs->sv_vfsp = vfsp; - /* - * We have a hold on the root vnode of the file system - * from the VFS_ROOT call above. - */ - smb_vfs->sv_rootvp = rootvp; - - smb_llist_insert_head(&se->e_vfs_list, smb_vfs); - DTRACE_PROBE1(smb_vfs_hold_miss, smb_vfs_t *, smb_vfs); - smb_llist_exit(&se->e_vfs_list); - - return (0); -} - -/* - * smb_vfs_rele - * - * Decrements the reference count of the fs passed in. If the reference count - * drops to zero the smb_vfs_t structure associated with the fs is freed. - */ -void -smb_vfs_rele(smb_export_t *se, vfs_t *vfsp) -{ - smb_vfs_t *smb_vfs; - - ASSERT(vfsp); - - smb_llist_enter(&se->e_vfs_list, RW_WRITER); - smb_vfs = smb_vfs_find(se, vfsp); - DTRACE_PROBE1(smb_vfs_release, smb_vfs_t *, smb_vfs); - if (smb_vfs) { - ASSERT(smb_vfs->sv_refcnt); - if (--smb_vfs->sv_refcnt == 0) { - smb_llist_remove(&se->e_vfs_list, smb_vfs); - smb_llist_exit(&se->e_vfs_list); - smb_vfs_destroy(smb_vfs); - return; - } - } - smb_llist_exit(&se->e_vfs_list); -} - -/* - * smb_vfs_rele_all() - * - * Release all holds on root vnodes of file systems which were taken - * due to the existence of at least one enabled share on the file system. - * Called at driver close time. - */ -void -smb_vfs_rele_all(smb_export_t *se) -{ - smb_vfs_t *smb_vfs; - - smb_llist_enter(&se->e_vfs_list, RW_WRITER); - while ((smb_vfs = smb_llist_head(&se->e_vfs_list)) != NULL) { - - ASSERT(smb_vfs->sv_magic == SMB_VFS_MAGIC); - DTRACE_PROBE1(smb_vfs_rele_all_hit, smb_vfs_t *, smb_vfs); - smb_llist_remove(&se->e_vfs_list, smb_vfs); - smb_vfs_destroy(smb_vfs); - } - smb_llist_exit(&se->e_vfs_list); -} - -/* - * Goes through the list of smb_vfs_t structure and returns the one matching - * the vnode passed in. If no match is found a NULL pointer is returned. - * - * The list of smb_vfs_t structures has to have been entered prior calling - * this function. - */ -static smb_vfs_t * -smb_vfs_find(smb_export_t *se, vfs_t *vfsp) -{ - smb_vfs_t *smb_vfs; - - smb_vfs = smb_llist_head(&se->e_vfs_list); - while (smb_vfs) { - ASSERT(smb_vfs->sv_magic == SMB_VFS_MAGIC); - if (smb_vfs->sv_vfsp == vfsp) - return (smb_vfs); - smb_vfs = smb_llist_next(&se->e_vfs_list, smb_vfs); - } - - return (NULL); -} - -static void -smb_vfs_destroy(smb_vfs_t *smb_vfs) -{ - VN_RELE(smb_vfs->sv_rootvp); - smb_vfs->sv_magic = (uint32_t)~SMB_VFS_MAGIC; - kmem_cache_free(smb_kshare_cache_vfs, smb_vfs); -} diff --git a/usr/src/uts/common/fs/smbsrv/smb_vops.c b/usr/src/uts/common/fs/smbsrv/smb_vops.c index d2f0fd7085..4b0f99839f 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_vops.c +++ b/usr/src/uts/common/fs/smbsrv/smb_vops.c @@ -608,8 +608,14 @@ smb_vop_lookup( char *np = name; char namebuf[MAXNAMELEN]; - if (*name == '\0') - return (EINVAL); + if (*name == '\0') { + /* + * This happens creating named streams at the share root. + */ + VN_HOLD(dvp); + *vpp = dvp; + return (0); + } ASSERT(vpp); *vpp = NULL; diff --git a/usr/src/uts/common/smbsrv/smb2_kproto.h b/usr/src/uts/common/smbsrv/smb2_kproto.h index 97b13af868..ed553bedcd 100644 --- a/usr/src/uts/common/smbsrv/smb2_kproto.h +++ b/usr/src/uts/common/smbsrv/smb2_kproto.h @@ -32,6 +32,7 @@ extern uint32_t smb2_dh_def_timeout; extern uint32_t smb2_dh_max_timeout; extern uint32_t smb2_res_def_timeout; extern uint32_t smb2_res_max_timeout; +extern uint32_t smb2_persist_timeout; extern int smb2_enable_dh; #define SMB3_CLIENT_ENCRYPTS(sr) \ @@ -131,7 +132,7 @@ uint32_t smb2_setinfo_quota(smb_request_t *, smb_setinfo_t *); void smb2_oplock_acquire(smb_request_t *sr); void smb2_oplock_reconnect(smb_request_t *sr); void smb2_lease_acquire(smb_request_t *sr); -uint32_t smb2_lease_create(smb_request_t *sr); +uint32_t smb2_lease_create(smb_request_t *sr, uint8_t *); void smb2_lease_rele(smb_lease_t *); void smb2_lease_init(void); void smb2_lease_fini(void); @@ -142,6 +143,15 @@ void smb2_durable_timers(smb_server_t *); uint32_t smb2_dh_reconnect(smb_request_t *); boolean_t smb_dh_should_save(smb_ofile_t *); extern void smb2_dh_shutdown(smb_server_t *); +int smb2_dh_new_ca_share(smb_server_t *, smb_kshare_t *); +void smb2_dh_close_persistent(smb_ofile_t *); +void smb2_dh_close_my_orphans(smb_request_t *, smb_ofile_t *); +int smb2_dh_make_persistent(smb_request_t *, smb_ofile_t *); +void smb2_dh_setdoc_persistent(smb_ofile_t *); +void smb2_dh_update_nvfile(smb_request_t *); +void smb2_dh_update_oplock(smb_request_t *, smb_ofile_t *); +void smb2_dh_update_locks(smb_request_t *, smb_ofile_t *); +void smb2_dh_update_times(smb_request_t *, smb_ofile_t *, smb_attr_t *); #ifdef __cplusplus } diff --git a/usr/src/uts/common/smbsrv/smb_kproto.h b/usr/src/uts/common/smbsrv/smb_kproto.h index d18ff80d5e..751f047e0c 100644 --- a/usr/src/uts/common/smbsrv/smb_kproto.h +++ b/usr/src/uts/common/smbsrv/smb_kproto.h @@ -338,6 +338,8 @@ boolean_t smb_validate_dirname(smb_request_t *, smb_pathname_t *); boolean_t smb_validate_object_name(smb_request_t *, smb_pathname_t *); boolean_t smb_validate_stream_name(smb_request_t *, smb_pathname_t *); boolean_t smb_is_stream_name(char *); +boolean_t smb_strname_restricted(char *); + void smb_stream_parse_name(char *, char *, char *); @@ -438,7 +440,7 @@ int smb_server_get_count(void); int smb_server_g_init(void); void smb_server_g_fini(void); int smb_server_create(void); -int smb_server_delete(void); +int smb_server_delete(smb_server_t *); int smb_server_configure(smb_ioc_cfg_t *); int smb_server_start(smb_ioc_start_t *); int smb_server_stop(void); @@ -451,7 +453,7 @@ int smb_server_numopen(smb_ioc_opennum_t *); int smb_server_enum(smb_ioc_svcenum_t *); int smb_server_session_close(smb_ioc_session_t *); int smb_server_file_close(smb_ioc_fileid_t *); -int smb_server_sharevp(smb_server_t *, const char *, vnode_t **); +int smb_server_share_lookup(smb_server_t *, const char *, smb_node_t **); int smb_server_unshare(const char *); void smb_server_logoff_ssnid(smb_request_t *, uint64_t); @@ -553,14 +555,6 @@ int smb_pathname(smb_request_t *, char *, int, smb_node_t *, smb_node_t *, smb_node_t **, smb_node_t **, cred_t *); /* - * smb_vfs functions - */ - -int smb_vfs_hold(smb_export_t *, vfs_t *); -void smb_vfs_rele(smb_export_t *, vfs_t *); -void smb_vfs_rele_all(smb_export_t *); - -/* * smb_notify.c */ uint32_t smb_notify_act1(smb_request_t *, uint32_t, uint32_t); @@ -633,6 +627,7 @@ smb_tree_t *smb_session_lookup_volume(smb_session_t *, const char *, 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_share(smb_session_t *, const char *); +void smb_session_logoff(smb_session_t *); 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); @@ -654,7 +649,7 @@ smb_ofile_t *smb_ofile_lookup_by_uniqid(smb_tree_t *, uint32_t); smb_ofile_t *smb_ofile_lookup_by_persistid(smb_request_t *, uint64_t); boolean_t smb_ofile_disallow_fclose(smb_ofile_t *); smb_ofile_t *smb_ofile_alloc(smb_request_t *, smb_arg_open_t *, smb_node_t *, - uint16_t, uint16_t, uint32_t); + uint16_t, uint16_t); void smb_ofile_open(smb_request_t *, smb_arg_open_t *, smb_ofile_t *); void smb_ofile_close(smb_ofile_t *, int32_t); void smb_ofile_free(smb_ofile_t *); @@ -678,7 +673,9 @@ void smb_delayed_write_timer(smb_llist_t *); void smb_ofile_set_quota_resume(smb_ofile_t *, char *); void smb_ofile_get_quota_resume(smb_ofile_t *, char *, int); void smb_ofile_del_persistid(smb_ofile_t *); -void smb_ofile_set_persistid(smb_ofile_t *); +void smb_ofile_set_persistid_dh(smb_ofile_t *); +void smb_ofile_set_persistid_ph(smb_ofile_t *); +int smb_ofile_insert_persistid(smb_ofile_t *, uint64_t); #define SMB_OFILE_GET_SESSION(of) ((of)->f_session) #define SMB_OFILE_GET_TREE(of) ((of)->f_tree) @@ -734,6 +731,7 @@ void smb_user_netinfo_fini(smb_netuserinfo_t *); int smb_user_netinfo_encode(smb_user_t *, uint8_t *, size_t, uint32_t *); smb_token_t *smb_get_token(smb_session_t *, smb_logon_t *); cred_t *smb_cred_create(smb_token_t *); +cred_t *smb_kcred_create(void); void smb_user_setcred(smb_user_t *, cred_t *, uint32_t); boolean_t smb_is_same_user(cred_t *, cred_t *); @@ -741,6 +739,7 @@ boolean_t smb_is_same_user(cred_t *, cred_t *); * SMB tree functions (file smb_tree.c) */ uint32_t smb_tree_connect(smb_request_t *); +uint32_t smb_tree_connect_disk(smb_request_t *, smb_arg_tcon_t *); void smb_tree_disconnect(smb_tree_t *, boolean_t); void smb_tree_close_pid(smb_tree_t *, uint32_t); boolean_t smb_tree_has_feature(smb_tree_t *, uint_t); @@ -751,6 +750,8 @@ 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 *); +smb_tree_t *smb_tree_alloc(smb_request_t *, const smb_kshare_t *, + smb_node_t *, uint32_t, uint32_t); smb_xa_t *smb_xa_create(smb_session_t *session, smb_request_t *sr, uint32_t total_parameter_count, uint32_t total_data_count, @@ -937,7 +938,7 @@ void smb_threshold_exit(smb_cmd_threshold_t *); void smb_threshold_wake_all(smb_cmd_threshold_t *); /* SMB hash function prototypes */ -smb_hash_t *smb_hash_create(size_t, size_t, uint32_t num_buckets); +smb_hash_t *smb_hash_create(size_t, size_t, uint32_t); void smb_hash_destroy(smb_hash_t *); uint_t smb_hash_uint64(smb_hash_t *, uint64_t); diff --git a/usr/src/uts/common/smbsrv/smb_ktypes.h b/usr/src/uts/common/smbsrv/smb_ktypes.h index 09e52b70f7..1f8ce704fb 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 2017 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -61,6 +61,7 @@ extern "C" { struct __door_handle; /* <sys/door.h> */ struct edirent; /* <sys/extdirent.h> */ +struct nvlist; struct smb_disp_entry; struct smb_request; @@ -476,7 +477,6 @@ typedef struct { typedef struct smb_export { kmutex_t e_mutex; boolean_t e_ready; - smb_llist_t e_vfs_list; smb_avl_t e_share_avl; smb_slist_t e_unexport_list; smb_thread_t e_unexport_thread; @@ -629,16 +629,6 @@ typedef struct smb_lease { uint8_t ls_clnt[SMB_LEASE_KEY_SZ]; } smb_lease_t; -#define SMB_VFS_MAGIC 0x534D4256 /* 'SMBV' */ - -typedef struct smb_vfs { - list_node_t sv_lnd; - uint32_t sv_magic; - uint32_t sv_refcnt; - vfs_t *sv_vfsp; - vnode_t *sv_rootvp; -} smb_vfs_t; - #define SMB_NODE_MAGIC 0x4E4F4445 /* 'NODE' */ #define SMB_NODE_VALID(p) ASSERT((p)->n_magic == SMB_NODE_MAGIC) @@ -703,6 +693,9 @@ typedef struct smb_node { typedef struct smb_kshare { uint32_t shr_magic; + avl_node_t shr_link; + kmutex_t shr_mutex; + kcondvar_t shr_cv; char *shr_name; char *shr_path; char *shr_cmnt; @@ -717,8 +710,9 @@ typedef struct smb_kshare { char *shr_access_none; char *shr_access_ro; char *shr_access_rw; - avl_node_t shr_link; - kmutex_t shr_mutex; + smb_node_t *shr_root_node; + smb_node_t *shr_ca_dir; + void *shr_import_busy; smb_cfg_val_t shr_encrypt; /* Share.EncryptData */ } smb_kshare_t; @@ -984,7 +978,7 @@ typedef struct smb_session { unsigned char MAC_key[44]; char ip_addr_str[INET6_ADDRSTRLEN]; uint8_t clnt_uuid[16]; - char workstation[SMB_PI_MAX_HOST]; + char workstation[SMB_PI_MAX_HOST]; } smb_session_t; /* @@ -1100,6 +1094,7 @@ typedef struct smb_user { #define SMB_TREE_SPARSE 0x00040000 #define SMB_TREE_TRAVERSE_MOUNTS 0x00080000 #define SMB_TREE_FORCE_L2_OPLOCK 0x00100000 +#define SMB_TREE_CA 0x00200000 /* Note: SMB_TREE_... in the mdb module too. */ /* @@ -1166,15 +1161,15 @@ typedef struct smb_tree { (((sr) && (sr)->tid_tree) ? \ (((sr)->tid_tree->t_access) & (acemask)) : 0))) -#define SMB_TREE_SUPPORTS_CATIA(sr) \ +#define SMB_TREE_SUPPORTS_CATIA(sr) \ (((sr) && (sr)->tid_tree) ? \ smb_tree_has_feature((sr)->tid_tree, SMB_TREE_CATIA) : 0) -#define SMB_TREE_SUPPORTS_ABE(sr) \ +#define SMB_TREE_SUPPORTS_ABE(sr) \ (((sr) && (sr)->tid_tree) ? \ smb_tree_has_feature((sr)->tid_tree, SMB_TREE_ABE) : 0) -#define SMB_TREE_IS_DFSROOT(sr) \ +#define SMB_TREE_IS_DFSROOT(sr) \ (((sr) && (sr)->tid_tree) ? \ smb_tree_has_feature((sr)->tid_tree, SMB_TREE_DFSROOT) : 0) @@ -1202,7 +1197,7 @@ typedef struct smb_tree { (SMB_TREE_IS_READONLY((sr)) || \ smb_node_file_is_readonly((node))) -#define SMB_ODIR_MAGIC 0x4F444952 /* 'ODIR' */ +#define SMB_ODIR_MAGIC 0x4F444952 /* 'ODIR' */ #define SMB_ODIR_VALID(p) \ ASSERT((p != NULL) && ((p)->d_magic == SMB_ODIR_MAGIC)) @@ -1332,7 +1327,7 @@ typedef struct smb_opipe { #define SMB_OFLAGS_SET_DELETE_ON_CLOSE 0x0004 #define SMB_OFLAGS_LLF_POS_VALID 0x0008 -#define SMB_OFILE_MAGIC 0x4F464C45 /* 'OFLE' */ +#define SMB_OFILE_MAGIC 0x4F464C45 /* 'OFLE' */ #define SMB_OFILE_VALID(p) \ ASSERT((p != NULL) && ((p)->f_magic == SMB_OFILE_MAGIC)) @@ -1416,6 +1411,10 @@ typedef struct smb_ofile { hrtime_t dh_timeout_offset; /* time offset for timeout */ hrtime_t dh_expire_time; /* time the handle expires */ boolean_t dh_persist; + kmutex_t dh_nvlock; + struct nvlist *dh_nvlist; + smb_node_t *dh_nvfile; + uint8_t dh_create_guid[16]; char f_quota_resume[SMB_SID_STRSZ]; uint8_t f_lock_seq[SMB_OFILE_LSEQ_MAX]; @@ -1441,7 +1440,7 @@ typedef struct smb_streaminfo { char si_name[MAXPATHLEN]; } smb_streaminfo_t; -#define SMB_LOCK_MAGIC 0x4C4F434B /* 'LOCK' */ +#define SMB_LOCK_MAGIC 0x4C4F434B /* 'LOCK' */ typedef struct smb_lock { list_node_t l_lnd; @@ -1472,7 +1471,7 @@ typedef struct smb_lock { typedef struct vardata_block { uint8_t vdb_tag; uint32_t vdb_len; - struct uio vdb_uio; + struct uio vdb_uio; struct iovec vdb_iovec[MAX_IOVEC]; } smb_vdb_t; @@ -1760,7 +1759,7 @@ typedef struct smb_arg_olbrk { * */ -#define SMB_REQ_MAGIC 0x534D4252 /* 'SMBR' */ +#define SMB_REQ_MAGIC 0x534D4252 /* 'SMBR' */ #define SMB_REQ_VALID(p) ASSERT((p)->sr_magic == SMB_REQ_MAGIC) typedef enum smb_req_state { @@ -1810,7 +1809,7 @@ typedef struct smb_request { list_t sr_storage; struct smb_xa *r_xa; int andx_prev_wct; - int cur_reply_offset; + int cur_reply_offset; int orig_request_hdr; unsigned int reply_seqnum; /* reply sequence number */ unsigned char first_smb_com; /* command code */ @@ -1868,6 +1867,7 @@ typedef struct smb_request { uint8_t nonce[16]; boolean_t encrypted; + boolean_t dh_nvl_dirty; boolean_t smb2_async; uint64_t smb2_async_id; @@ -2068,7 +2068,7 @@ typedef enum smb_server_state { typedef struct { /* protected by sv_mutex */ kcondvar_t sp_cv; - uint32_t sp_cnt; + uint32_t sp_cnt; smb_llist_t sp_list; smb_llist_t sp_fidlist; } smb_spool_t; @@ -2094,11 +2094,12 @@ typedef struct smb_server { krwlock_t sv_cfg_lock; smb_kmod_cfg_t sv_cfg; smb_session_t *sv_session; + smb_user_t *sv_rootuser; smb_llist_t sv_session_list; smb_hash_t *sv_persistid_ht; smb_hash_t *sv_lease_ht; - struct smb_export sv_export; + smb_export_t sv_export; struct __door_handle *sv_lmshrd; /* Internal door for up-calls to smbd */ diff --git a/usr/src/uts/common/smbsrv/smb_share.h b/usr/src/uts/common/smbsrv/smb_share.h index 7c2219caad..090de59105 100644 --- a/usr/src/uts/common/smbsrv/smb_share.h +++ b/usr/src/uts/common/smbsrv/smb_share.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2017 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. */ @@ -92,6 +92,7 @@ extern "C" { #define SHOPT_AD_CONTAINER "ad-container" #define SHOPT_ABE "abe" #define SHOPT_NAME "name" +#define SHOPT_CA "ca" #define SHOPT_CSC "csc" #define SHOPT_CATIA "catia" #define SHOPT_GUEST "guestok" @@ -185,6 +186,7 @@ extern "C" { #define SMB_SHRF_QUOTAS 0x1000 /* Enable SMB Quotas */ #define SMB_SHRF_FSO 0x2000 /* Force Shared Oplocks */ +#define SMB_SHRF_CA 0x4000 /* Continuous Availability */ /* * Runtime flags @@ -193,6 +195,7 @@ extern "C" { #define SMB_SHRF_TRANS 0x10000000 #define SMB_SHRF_PERM 0x20000000 #define SMB_SHRF_AUTOHOME 0x40000000 +#define SMB_SHRF_REMOVED 0x80000000 /* unshared */ #define SMB_SHARE_PRINT "print$" #define SMB_SHARE_PRINT_LEN 6 |