#include <sys/refstr_impl.h>
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_ktypes.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smb_share.h>
int smb_tcon_mute = 1;
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 void smb_tree_dealloc(void *);
static boolean_t smb_tree_is_connected_locked(smb_tree_t *);
static char *smb_tree_get_sharename(char *);
static int smb_tree_getattr(const smb_kshare_t *, smb_node_t *, smb_tree_t *);
static void smb_tree_get_creation(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 *, 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 *);
static void smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *);
static void smb_tree_netinfo_fini(smb_netconnectinfo_t *);
uint32_t
smb_tree_connect(smb_request_t *sr)
{
smb_server_t *sv = sr->sr_server;
uint32_t status;
if (smb_threshold_enter(&sv->sv_tcon_ct) != 0) {
return (NT_STATUS_INSUFF_SERVER_RESOURCES);
}
status = smb_tree_connect_core(sr);
smb_threshold_exit(&sv->sv_tcon_ct);
return (status);
}
uint32_t
smb_tree_connect_core(smb_request_t *sr)
{
smb_arg_tcon_t *tcon = &sr->sr_tcon;
smb_kshare_t *si;
char *name;
uint32_t status;
(void) smb_strlwr(tcon->path);
if ((name = smb_tree_get_sharename(tcon->path)) == NULL) {
smb_tree_log(sr, tcon->path, "invalid UNC path");
return (NT_STATUS_BAD_NETWORK_NAME);
}
si = smb_kshare_lookup(sr->sr_server, name);
if (si == NULL) {
smb_tree_log(sr, name, "share not found");
return (NT_STATUS_BAD_NETWORK_NAME);
}
if (!strcasecmp(SMB_SHARE_PRINT, name)) {
smb_kshare_release(sr->sr_server, si);
smb_tree_log(sr, name, "access not permitted");
return (NT_STATUS_ACCESS_DENIED);
}
tcon->name = name;
sr->sr_tcon.si = si;
if ((sr->sr_server->sv_cfg.skc_encrypt == SMB_CONFIG_REQUIRED ||
si->shr_encrypt == SMB_CONFIG_REQUIRED) &&
(sr->session->srv_cap & SMB2_CAP_ENCRYPTION) == 0) {
status = NT_STATUS_ACCESS_DENIED;
goto out;
}
switch (si->shr_type & STYPE_MASK) {
case STYPE_DISKTREE:
status = smb_tree_connect_disk(sr, &sr->sr_tcon);
break;
case STYPE_IPC:
status = smb_tree_connect_ipc(sr, &sr->sr_tcon);
break;
case STYPE_PRINTQ:
status = smb_tree_connect_printq(sr, &sr->sr_tcon);
break;
default:
status = NT_STATUS_BAD_DEVICE_TYPE;
break;
}
out:
smb_kshare_release(sr->sr_server, si);
sr->sr_tcon.si = NULL;
return (status);
}
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);
mutex_enter(&tree->t_mutex);
ASSERT(tree->t_refcnt);
if (!smb_tree_is_connected_locked(tree)) {
mutex_exit(&tree->t_mutex);
return;
}
tree->t_state = SMB_TREE_STATE_DISCONNECTING;
mutex_exit(&tree->t_mutex);
smb_ofile_close_all(tree, 0);
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);
}
}
boolean_t
smb_tree_hold(
smb_tree_t *tree)
{
SMB_TREE_VALID(tree);
mutex_enter(&tree->t_mutex);
if (smb_tree_is_connected_locked(tree)) {
tree->t_refcnt++;
mutex_exit(&tree->t_mutex);
return (B_TRUE);
}
mutex_exit(&tree->t_mutex);
return (B_FALSE);
}
void
smb_tree_hold_internal(
smb_tree_t *tree)
{
SMB_TREE_VALID(tree);
mutex_enter(&tree->t_mutex);
tree->t_refcnt++;
mutex_exit(&tree->t_mutex);
}
void
smb_tree_release(
smb_tree_t *tree)
{
SMB_TREE_VALID(tree);
smb_lavl_flush(&tree->t_ofile_list);
smb_llist_flush(&tree->t_odir_list);
mutex_enter(&tree->t_mutex);
ASSERT(tree->t_refcnt);
tree->t_refcnt--;
switch (tree->t_state) {
case SMB_TREE_STATE_DISCONNECTING:
if (tree->t_refcnt == 0) {
smb_session_t *ssn = tree->t_session;
tree->t_state = SMB_TREE_STATE_DISCONNECTED;
smb_llist_post(&ssn->s_tree_list, tree,
smb_tree_dealloc);
}
break;
case SMB_TREE_STATE_CONNECTED:
break;
default:
ASSERT(0);
break;
}
mutex_exit(&tree->t_mutex);
}
void
smb_tree_close_pid(
smb_tree_t *tree,
uint32_t pid)
{
ASSERT(tree);
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
smb_ofile_close_all(tree, pid);
smb_tree_close_odirs(tree, pid);
}
boolean_t
smb_tree_has_feature(smb_tree_t *tree, uint32_t flags)
{
ASSERT(tree);
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
return ((tree->t_flags & flags) == flags);
}
int
smb_tree_enum(smb_tree_t *tree, smb_svcenum_t *svcenum)
{
smb_lavl_t *lavl;
smb_ofile_t *of;
int rc = 0;
if (svcenum->se_type == SMB_SVCENUM_TYPE_TREE)
return (smb_tree_enum_private(tree, svcenum));
lavl = &tree->t_ofile_list;
smb_lavl_enter(lavl, RW_READER);
of = smb_lavl_first(lavl);
while (of) {
if (smb_ofile_hold(of)) {
rc = smb_ofile_enum(of, svcenum);
smb_ofile_release(of);
}
if (rc != 0)
break;
of = smb_lavl_next(lavl, of);
}
smb_lavl_exit(lavl);
return (rc);
}
int
smb_tree_fclose(smb_tree_t *tree, uint32_t uniqid)
{
smb_ofile_t *of;
ASSERT(tree);
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
if ((of = smb_ofile_lookup_by_uniqid(tree, uniqid)) == NULL)
return (ENOENT);
if (smb_ofile_disallow_fclose(of)) {
smb_ofile_release(of);
return (EACCES);
}
smb_ofile_close(of, 0);
smb_ofile_release(of);
return (0);
}
#define SHARES_DIR ".zfs/shares/"
static uint32_t
smb_tree_acl_access(smb_request_t *sr, const smb_kshare_t *si, vnode_t *pathvp)
{
smb_user_t *user;
cred_t *cred;
int rc;
vfs_t *vfsp;
vnode_t *root = NULL;
vnode_t *sharevp = NULL;
char *sharepath;
struct pathname pnp;
size_t size;
uint32_t access;
user = sr->uid_user;
cred = user->u_cred;
access = ACE_ALL_PERMS;
if (si->shr_flags & SMB_SHRF_AUTOHOME) {
if (si->shr_uid != crgetuid(cred))
access = 0;
return (access);
}
vfsp = pathvp->v_vfsp;
if (vfsp != NULL)
rc = VFS_ROOT(vfsp, &root);
else
rc = ENOENT;
if (rc != 0)
return (access);
size = sizeof (SHARES_DIR) + strlen(si->shr_name) + 1;
sharepath = smb_srm_alloc(sr, size);
(void) snprintf(sharepath, size, "%s%s", SHARES_DIR, si->shr_name);
pn_alloc(&pnp);
(void) pn_set(&pnp, sharepath);
rc = lookuppnvp(&pnp, NULL, NO_FOLLOW, NULL, &sharevp, rootdir, root,
zone_kcred());
pn_free(&pnp);
if (rc == 0) {
smb_vop_eaccess(sharevp, (int *)&access, V_ACE_MASK, NULL,
cred);
VN_RELE(sharevp);
}
return (access);
}
static uint32_t
smb_tree_chkaccess(smb_request_t *sr, smb_kshare_t *shr, vnode_t *vp)
{
smb_user_t *user = sr->uid_user;
char *sharename = shr->shr_name;
uint32_t host_access;
uint32_t acl_access;
uint32_t access;
if (user->u_flags & SMB_USER_FLAG_ANON) {
smb_tree_log(sr, sharename, "access denied: IPC only");
return (0);
}
if ((user->u_flags & SMB_USER_FLAG_GUEST) &&
((shr->shr_flags & SMB_SHRF_GUEST_OK) == 0)) {
smb_tree_log(sr, sharename, "access denied: guest disabled");
return (0);
}
if ((shr->shr_flags & SMB_SHRF_ADMIN) && !SMB_USER_IS_ADMIN(user)) {
smb_tree_log(sr, sharename, "access denied: not admin");
return (0);
}
host_access = smb_kshare_hostaccess(shr, sr->session);
if ((host_access & ACE_ALL_PERMS) == 0) {
smb_tree_log(sr, sharename, "access denied: host access");
return (0);
}
acl_access = smb_tree_acl_access(sr, shr, vp);
if ((acl_access & ACE_ALL_PERMS) == 0) {
smb_tree_log(sr, sharename, "access denied: share ACL");
return (0);
}
access = host_access & acl_access;
if ((access & ACE_ALL_PERMS) == 0) {
smb_tree_log(sr, sharename, "access denied");
return (0);
}
return (access);
}
int smb_tcon_import_wait = 20;
uint32_t
smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon)
{
char *sharename = tcon->path;
const char *any = "?????";
smb_user_t *user = sr->uid_user;
smb_node_t *snode = NULL;
smb_kshare_t *si = tcon->si;
char *service = tcon->service;
smb_tree_t *tree;
int rc;
uint32_t access;
smb_shr_execinfo_t execinfo;
clock_t time;
ASSERT(user);
ASSERT(user->u_cred);
if (service != NULL &&
strcmp(service, any) != 0 &&
strcasecmp(service, "A:") != 0) {
smb_tree_log(sr, sharename, "invalid service (%s)", service);
return (NT_STATUS_BAD_DEVICE_TYPE);
}
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) {
return (NT_STATUS_ACCESS_DENIED);
}
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);
tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
switch (si->shr_flags & SMB_SHRF_CSC_MASK) {
case SMB_SHRF_CSC_DISABLED:
tcon->optional_support |= SMB_CSC_CACHE_NONE;
break;
case SMB_SHRF_CSC_AUTO:
tcon->optional_support |= SMB_CSC_CACHE_AUTO_REINT;
break;
case SMB_SHRF_CSC_VDO:
tcon->optional_support |= SMB_CSC_CACHE_VDO;
break;
case SMB_SHRF_CSC_MANUAL:
default:
break;
}
if (si->shr_flags & SMB_SHRF_ABE)
tcon->optional_support |=
SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM;
if (si->shr_flags & SMB_SHRF_DFSROOT)
tcon->optional_support |= SMB_SHARE_IS_IN_DFS;
tree = smb_tree_alloc(sr, si, snode, access, sr->sr_cfg->skc_execflags);
if (tree == NULL)
return (NT_STATUS_INSUFF_SERVER_RESOURCES);
if (tree->t_flags & SMB_TREE_SHORTNAMES)
tcon->optional_support |= SMB_UNIQUE_FILE_NAME;
if (tree->t_execflags & SMB_EXEC_MAP) {
smb_tree_set_execinfo(tree, &execinfo, SMB_EXEC_MAP);
rc = smb_kshare_exec(tree->t_server, &execinfo);
if ((rc != 0) && (tree->t_execflags & SMB_EXEC_TERM)) {
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);
}
}
sr->tid_tree = tree;
sr->smb_tid = tree->t_tid;
return (0);
}
uint32_t
smb_tree_connect_printq(smb_request_t *sr, smb_arg_tcon_t *tcon)
{
char *sharename = tcon->path;
const char *any = "?????";
smb_user_t *user = sr->uid_user;
smb_node_t *dnode = NULL;
smb_node_t *snode = NULL;
smb_kshare_t *si = tcon->si;
char *service = tcon->service;
char last_component[MAXNAMELEN];
smb_tree_t *tree;
int rc;
uint32_t access;
ASSERT(user);
ASSERT(user->u_cred);
if (sr->sr_server->sv_cfg.skc_print_enable == 0) {
smb_tree_log(sr, sharename, "printing disabled");
return (NT_STATUS_BAD_NETWORK_NAME);
}
if (service != NULL &&
strcmp(service, any) != 0 &&
strcasecmp(service, "LPT1:") != 0) {
smb_tree_log(sr, sharename, "invalid service (%s)", service);
return (NT_STATUS_BAD_DEVICE_TYPE);
}
rc = smb_pathname_reduce(sr, user->u_cred, si->shr_path, 0, 0, &dnode,
last_component);
if (rc == 0) {
rc = smb_fsop_lookup(sr, user->u_cred, SMB_FOLLOW_LINKS,
sr->sr_server->si_root_smb_node, dnode, last_component,
&snode);
smb_node_release(dnode);
}
if (rc) {
if (snode)
smb_node_release(snode);
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);
}
tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
tree = smb_tree_alloc(sr, si, snode, access, sr->sr_cfg->skc_execflags);
smb_node_release(snode);
if (tree == NULL)
return (NT_STATUS_INSUFF_SERVER_RESOURCES);
sr->tid_tree = tree;
sr->smb_tid = tree->t_tid;
return (0);
}
uint32_t
smb_tree_connect_ipc(smb_request_t *sr, smb_arg_tcon_t *tcon)
{
char *name = tcon->path;
const char *any = "?????";
smb_user_t *user = sr->uid_user;
smb_tree_t *tree;
smb_kshare_t *si = tcon->si;
char *service = tcon->service;
ASSERT(user);
if (service != NULL &&
strcmp(service, any) != 0 &&
strcasecmp(service, "IPC") != 0) {
smb_tree_log(sr, name, "invalid service (%s)", service);
return (NT_STATUS_BAD_DEVICE_TYPE);
}
if ((user->u_flags & SMB_USER_FLAG_ANON) &&
sr->sr_cfg->skc_restrict_anon) {
smb_tree_log(sr, name, "access denied: restrict anonymous");
return (NT_STATUS_ACCESS_DENIED);
}
tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
tree = smb_tree_alloc(sr, si, NULL, ACE_ALL_PERMS, 0);
if (tree == NULL)
return (NT_STATUS_INSUFF_SERVER_RESOURCES);
sr->tid_tree = tree;
sr->smb_tid = tree->t_tid;
return (0);
}
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)
{
smb_session_t *session = sr->session;
smb_tree_t *tree;
uint32_t stype = si->shr_type;
uint16_t tid;
if (smb_idpool_alloc(&session->s_tid_pool, &tid))
return (NULL);
tree = kmem_cache_alloc(smb_cache_tree, KM_SLEEP);
bzero(tree, sizeof (smb_tree_t));
tree->t_session = session;
tree->t_server = session->s_server;
if (STYPE_ISDSK(stype) || STYPE_ISPRN(stype)) {
if (smb_tree_getattr(si, snode, tree) != 0) {
smb_idpool_free(&session->s_tid_pool, tid);
kmem_cache_free(smb_cache_tree, tree);
return (NULL);
}
}
if (smb_idpool_constructor(&tree->t_fid_pool)) {
smb_idpool_free(&session->s_tid_pool, tid);
kmem_cache_free(smb_cache_tree, tree);
return (NULL);
}
if (smb_idpool_constructor(&tree->t_odid_pool)) {
smb_idpool_destructor(&tree->t_fid_pool);
smb_idpool_free(&session->s_tid_pool, tid);
kmem_cache_free(smb_cache_tree, tree);
return (NULL);
}
smb_lavl_constructor(&tree->t_ofile_list,
smb_ofile_avl_compare, sizeof (smb_ofile_t),
offsetof(smb_ofile_t, f_tree_lnd));
smb_llist_constructor(&tree->t_odir_list, sizeof (smb_odir_t),
offsetof(smb_odir_t, d_lnd));
(void) strlcpy(tree->t_sharename, si->shr_name,
sizeof (tree->t_sharename));
(void) strlcpy(tree->t_resource, si->shr_path,
sizeof (tree->t_resource));
mutex_init(&tree->t_mutex, NULL, MUTEX_DEFAULT, NULL);
tree->t_refcnt = 1;
tree->t_tid = tid;
tree->t_res_type = stype;
tree->t_state = SMB_TREE_STATE_CONNECTED;
tree->t_magic = SMB_TREE_MAGIC;
tree->t_access = access;
tree->t_connect_time = gethrestime_sec();
tree->t_execflags = execflags;
smb_user_hold_internal(sr->uid_user);
smb_user_inc_trees(sr->uid_user);
tree->t_owner = sr->uid_user;
if (tree->t_flags & SMB_TREE_READONLY)
tree->t_access &= ~ACE_ALL_WRITE_PERMS;
if (STYPE_ISDSK(stype) || STYPE_ISPRN(stype)) {
smb_node_ref(snode);
tree->t_snode = snode;
tree->t_acltype = smb_fsop_acltype(snode);
}
smb_llist_enter(&session->s_tree_list, RW_WRITER);
smb_llist_insert_head(&session->s_tree_list, tree);
smb_llist_exit(&session->s_tree_list);
atomic_inc_32(&session->s_tree_cnt);
smb_server_inc_trees(session->s_server);
return (tree);
}
static void
smb_tree_dealloc(void *arg)
{
smb_session_t *session;
smb_tree_t *tree = (smb_tree_t *)arg;
SMB_TREE_VALID(tree);
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);
smb_idpool_free(&session->s_tid_pool, tree->t_tid);
atomic_dec_32(&session->s_tree_cnt);
smb_llist_exit(&session->s_tree_list);
mutex_enter(&tree->t_mutex);
mutex_exit(&tree->t_mutex);
tree->t_magic = (uint32_t)~SMB_TREE_MAGIC;
if (tree->t_snode)
smb_node_release(tree->t_snode);
mutex_destroy(&tree->t_mutex);
smb_lavl_destructor(&tree->t_ofile_list);
smb_llist_destructor(&tree->t_odir_list);
smb_idpool_destructor(&tree->t_fid_pool);
smb_idpool_destructor(&tree->t_odid_pool);
SMB_USER_VALID(tree->t_owner);
smb_user_dec_trees(tree->t_owner);
smb_user_release(tree->t_owner);
kmem_cache_free(smb_cache_tree, tree);
}
static boolean_t
smb_tree_is_connected_locked(smb_tree_t *tree)
{
switch (tree->t_state) {
case SMB_TREE_STATE_CONNECTED:
return (B_TRUE);
case SMB_TREE_STATE_DISCONNECTING:
case SMB_TREE_STATE_DISCONNECTED:
return (B_FALSE);
default:
ASSERT(0);
return (B_FALSE);
}
}
static char *
smb_tree_get_sharename(char *unc_path)
{
char *sharename = unc_path;
if (sharename[0] == '\\') {
if (sharename[1] != '\\')
return (NULL);
if ((sharename = strchr(sharename+2, '\\')) == NULL)
return (NULL);
++sharename;
} else if (strchr(sharename, '\\') != NULL) {
return (NULL);
}
return (sharename);
}
static int
smb_tree_getattr(const smb_kshare_t *si, smb_node_t *node, smb_tree_t *tree)
{
vfs_t *vfsp = SMB_NODE_VFS(node);
vfs_t *realvfsp;
ASSERT(vfsp);
smb_tree_get_creation(node, tree);
smb_tree_get_volname(vfsp, tree);
realvfsp = getvfs(&vfsp->vfs_fsid);
if (realvfsp != NULL) {
smb_tree_get_flags(si, realvfsp, tree);
VFS_RELE(realvfsp);
} else {
cmn_err(CE_NOTE, "Failed getting info for share: %s",
si->shr_name);
smb_tree_get_flags(si, vfsp, tree);
}
if (tree->t_session->dialect >= SMB_VERS_3_0)
tree->t_encrypt = si->shr_encrypt;
else
tree->t_encrypt = SMB_CONFIG_DISABLED;
return (0);
}
static void
smb_tree_get_creation(smb_node_t *node, smb_tree_t *tree)
{
smb_attr_t attr;
cred_t *kcr = zone_kcred();
bzero(&attr, sizeof (attr));
attr.sa_mask = SMB_AT_CRTIME;
(void) smb_node_getattr(NULL, node, kcr, NULL, &attr);
tree->t_create_time = attr.sa_crtime;
}
static void
smb_tree_get_volname(vfs_t *vfsp, smb_tree_t *tree)
{
#ifdef _FAKE_KERNEL
_NOTE(ARGUNUSED(vfsp))
(void) strlcpy(tree->t_volume, "fake", SMB_VOLNAMELEN);
#else
refstr_t *vfs_mntpoint;
const char *s;
char *name;
vfs_mntpoint = vfs_getmntpoint(vfsp);
s = refstr_value(vfs_mntpoint);
s += strspn(s, "/");
(void) strlcpy(tree->t_volume, s, SMB_VOLNAMELEN);
refstr_rele(vfs_mntpoint);
name = tree->t_volume;
(void) strsep((char **)&name, "/");
#endif
}
static void
smb_tree_get_flags(const smb_kshare_t *si, vfs_t *vfsp, smb_tree_t *tree)
{
smb_session_t *ssn = tree->t_session;
struct vfssw *vswp;
typedef struct smb_mtype {
char *mt_name;
size_t mt_namelen;
uint32_t mt_flags;
} smb_mtype_t;
static smb_mtype_t smb_mtype[] = {
#ifdef _FAKE_KERNEL
{ "fake", 3, SMB_TREE_SPARSE},
#endif
{ "zfs", 3, SMB_TREE_QUOTA | SMB_TREE_SPARSE},
{ "ufs", 3, 0 },
{ "nfs", 3, SMB_TREE_NFS_MOUNTED },
{ "tmpfs", 5, SMB_TREE_NO_EXPORT }
};
smb_mtype_t *mtype;
char *name;
uint32_t flags =
SMB_TREE_SUPPORTS_ACLS |
SMB_TREE_UNICODE_ON_DISK;
int i;
if (si->shr_flags & SMB_SHRF_DFSROOT)
flags |= SMB_TREE_DFSROOT;
if (si->shr_flags & SMB_SHRF_CATIA)
flags |= SMB_TREE_CATIA;
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;
if (ssn->s_cfg.skc_oplock_enable)
flags |= SMB_TREE_OPLOCKS;
if (ssn->s_cfg.skc_traverse_mounts)
flags |= SMB_TREE_TRAVERSE_MOUNTS;
if (ssn->s_cfg.skc_short_names)
flags |= SMB_TREE_SHORTNAMES;
if (vfsp->vfs_flag & VFS_RDONLY)
flags |= SMB_TREE_READONLY;
if (vfsp->vfs_flag & VFS_XATTR)
flags |= SMB_TREE_STREAMS;
vswp = vfs_getvfsswbyvfsops(vfs_getops(vfsp));
if (vswp != NULL) {
name = vswp->vsw_name;
vfs_unrefvfssw(vswp);
} else {
name = "?";
}
for (i = 0; i < sizeof (smb_mtype) / sizeof (smb_mtype[0]); ++i) {
mtype = &smb_mtype[i];
if (strncasecmp(name, mtype->mt_name, mtype->mt_namelen) == 0)
flags |= mtype->mt_flags;
}
if ((si->shr_flags & SMB_SHRF_QUOTAS) == 0)
flags &= ~SMB_TREE_QUOTA;
(void) strlcpy(tree->t_typename, name, SMB_TYPENAMELEN);
(void) smb_strupr((char *)tree->t_typename);
if (vfs_has_feature(vfsp, VFSFT_XVATTR))
flags |= SMB_TREE_XVATTR;
if (vfs_has_feature(vfsp, VFSFT_CASEINSENSITIVE))
flags |= SMB_TREE_CASEINSENSITIVE;
if (vfs_has_feature(vfsp, VFSFT_NOCASESENSITIVE))
flags |= SMB_TREE_NO_CASESENSITIVE;
if (vfs_has_feature(vfsp, VFSFT_DIRENTFLAGS))
flags |= SMB_TREE_DIRENTFLAGS;
if (vfs_has_feature(vfsp, VFSFT_ACLONCREATE))
flags |= SMB_TREE_ACLONCREATE;
if (vfs_has_feature(vfsp, VFSFT_ACEMASKONACCESS))
flags |= SMB_TREE_ACEMASKONACCESS;
DTRACE_PROBE2(smb__tree__flags, uint32_t, flags, char *, name);
tree->t_flags = flags;
}
static void
smb_tree_log(smb_request_t *sr, const char *sharename, const char *fmt, ...)
{
va_list ap;
char buf[128];
smb_user_t *user = sr->uid_user;
ASSERT(user);
if (smb_tcon_mute)
return;
if ((user->u_name) && (strcasecmp(sharename, "IPC$") == 0)) {
if ((strcmp(user->u_name, "root") == 0) ||
(strcmp(user->u_name, "nobody") == 0)) {
return;
}
}
va_start(ap, fmt);
(void) vsnprintf(buf, 128, fmt, ap);
va_end(ap);
cmn_err(CE_NOTE, "smbd[%s\\%s]: %s %s",
user->u_domain, user->u_name, sharename, buf);
}
smb_odir_t *
smb_tree_lookup_odir(smb_request_t *sr, uint16_t odid)
{
smb_odir_t *od;
smb_llist_t *od_list;
smb_tree_t *tree = sr->tid_tree;
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
od_list = &tree->t_odir_list;
smb_llist_enter(od_list, RW_READER);
od = smb_llist_head(od_list);
while (od) {
if (od->d_odid == odid)
break;
od = smb_llist_next(od_list, od);
}
if (od == NULL)
goto out;
if (od->d_user != sr->uid_user) {
od = NULL;
goto out;
}
if (!smb_odir_hold(od))
od = NULL;
out:
smb_llist_exit(od_list);
return (od);
}
boolean_t
smb_tree_is_connected(smb_tree_t *tree)
{
boolean_t rb;
mutex_enter(&tree->t_mutex);
rb = smb_tree_is_connected_locked(tree);
mutex_exit(&tree->t_mutex);
return (rb);
}
static void
smb_tree_close_odirs(smb_tree_t *tree, uint32_t pid)
{
smb_llist_t *od_list;
smb_odir_t *od;
ASSERT(tree);
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
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);
if (pid != 0 && od->d_opened_by_pid != pid)
continue;
if (smb_odir_hold(od)) {
smb_odir_close(od);
smb_odir_release(od);
}
}
smb_llist_exit(od_list);
}
static void
smb_tree_set_execinfo(smb_tree_t *tree, smb_shr_execinfo_t *exec,
int exec_type)
{
exec->e_sharename = tree->t_sharename;
exec->e_winname = tree->t_owner->u_name;
exec->e_userdom = tree->t_owner->u_domain;
exec->e_srv_ipaddr = tree->t_session->local_ipaddr;
exec->e_cli_ipaddr = tree->t_session->ipaddr;
exec->e_cli_netbiosname = tree->t_session->workstation;
exec->e_uid = crgetuid(tree->t_owner->u_cred);
exec->e_type = exec_type;
}
static int
smb_tree_enum_private(smb_tree_t *tree, smb_svcenum_t *svcenum)
{
uint8_t *pb;
uint_t nbytes;
int rc;
if (svcenum->se_nskip > 0) {
svcenum->se_nskip--;
return (0);
}
if (svcenum->se_nitems >= svcenum->se_nlimit) {
svcenum->se_nitems = svcenum->se_nlimit;
return (0);
}
pb = &svcenum->se_buf[svcenum->se_bused];
rc = smb_tree_netinfo_encode(tree, pb, svcenum->se_bavail, &nbytes);
if (rc == 0) {
svcenum->se_bavail -= nbytes;
svcenum->se_bused += nbytes;
svcenum->se_nitems++;
}
return (rc);
}
static int
smb_tree_netinfo_encode(smb_tree_t *tree, uint8_t *buf, size_t buflen,
uint32_t *nbytes)
{
smb_netconnectinfo_t info;
int rc;
smb_tree_netinfo_init(tree, &info);
rc = smb_netconnectinfo_encode(&info, buf, buflen, nbytes);
smb_tree_netinfo_fini(&info);
return (rc);
}
static void
smb_tree_netinfo_username(smb_tree_t *tree, char **namestr, uint32_t *namelen)
{
smb_user_t *user = tree->t_owner;
ASSERT(namestr);
ASSERT(namelen);
ASSERT(user->u_domain_len > 0);
ASSERT(user->u_name_len > 0);
*namelen = user->u_domain_len + user->u_name_len;
*namestr = kmem_alloc(*namelen, KM_SLEEP);
(void) snprintf(*namestr, *namelen, "%s\\%s", user->u_domain,
user->u_name);
}
static void
smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *info)
{
ASSERT(tree);
info->ci_id = tree->t_tid;
info->ci_type = tree->t_res_type;
info->ci_numopens = tree->t_open_files;
info->ci_numusers = tree->t_refcnt;
info->ci_time = gethrestime_sec() - tree->t_connect_time;
info->ci_sharelen = strlen(tree->t_sharename) + 1;
info->ci_share = smb_mem_strdup(tree->t_sharename);
smb_tree_netinfo_username(tree, &info->ci_username, &info->ci_namelen);
}
static void
smb_tree_netinfo_fini(smb_netconnectinfo_t *info)
{
if (info == NULL)
return;
if (info->ci_username)
kmem_free(info->ci_username, info->ci_namelen);
if (info->ci_share)
smb_mem_free(info->ci_share);
bzero(info, sizeof (smb_netconnectinfo_t));
}