/* * Deallocate a tree: release all resources associated with a tree and * remove the tree from the user's tree list. * * The tree being destroyed must be in the "destroying" state and the * reference count must be zero. This function assumes it's single threaded * i.e. only one thread will attempt to destroy a specific tree, which * should be the case if the tree is in disconnected and has a reference * count of zero. */ static void smb_tree_dealloc(smb_tree_t *tree) { ASSERT(tree); ASSERT(tree->t_magic == SMB_TREE_MAGIC); ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED); ASSERT(tree->t_refcnt == 0); /* * Remove the tree from the user's tree list. This must be done * before any resources associated with the tree are released. */ smb_llist_enter(&tree->t_user->u_tree_list, RW_WRITER); smb_llist_remove(&tree->t_user->u_tree_list, tree); smb_llist_exit(&tree->t_user->u_tree_list); tree->t_magic = (uint32_t)~SMB_TREE_MAGIC; smb_idpool_free(&tree->t_user->u_tid_pool, tree->t_tid); atomic_dec_32(&tree->t_session->s_tree_cnt); if (tree->t_snode) smb_node_release(tree->t_snode); mutex_destroy(&tree->t_mutex); /* * The list of open files and open directories should be empty. */ smb_llist_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); kmem_cache_free(tree->t_server->si_cache_tree, tree); }
/* * Delete an odir. * * Remove the odir from the tree list before freeing resources * associated with the odir. */ void smb_odir_delete(void *arg) { smb_tree_t *tree; smb_odir_t *od = (smb_odir_t *)arg; SMB_ODIR_VALID(od); ASSERT(od->d_refcnt == 0); ASSERT(od->d_state == SMB_ODIR_STATE_CLOSED); tree = od->d_tree; smb_llist_enter(&tree->t_odir_list, RW_WRITER); smb_llist_remove(&tree->t_odir_list, od); smb_idpool_free(&tree->t_odid_pool, od->d_odid); atomic_dec_32(&tree->t_session->s_dir_cnt); smb_llist_exit(&tree->t_odir_list); mutex_enter(&od->d_mutex); mutex_exit(&od->d_mutex); od->d_magic = 0; smb_node_release(od->d_dnode); smb_user_release(od->d_user); mutex_destroy(&od->d_mutex); kmem_cache_free(smb_cache_odir, od); }
/* * Delete an ofile. * * Remove the ofile from the tree list before freeing resources * associated with the ofile. */ void smb_ofile_delete(void *arg) { smb_tree_t *tree; smb_ofile_t *of = (smb_ofile_t *)arg; SMB_OFILE_VALID(of); ASSERT(of->f_refcnt == 0); ASSERT(of->f_state == SMB_OFILE_STATE_CLOSED); ASSERT(!SMB_OFILE_OPLOCK_GRANTED(of)); tree = of->f_tree; smb_llist_enter(&tree->t_ofile_list, RW_WRITER); smb_llist_remove(&tree->t_ofile_list, of); smb_idpool_free(&tree->t_fid_pool, of->f_fid); atomic_dec_32(&tree->t_session->s_file_cnt); smb_llist_exit(&tree->t_ofile_list); mutex_enter(&of->f_mutex); mutex_exit(&of->f_mutex); switch (of->f_ftype) { case SMB_FTYPE_BYTE_PIPE: case SMB_FTYPE_MESG_PIPE: smb_opipe_dealloc(of->f_pipe); of->f_pipe = NULL; break; case SMB_FTYPE_DISK: if (of->f_odir != NULL) smb_odir_release(of->f_odir); smb_node_rem_ofile(of->f_node, of); smb_node_release(of->f_node); break; default: ASSERT(!"f_ftype"); break; } of->f_magic = (uint32_t)~SMB_OFILE_MAGIC; mutex_destroy(&of->f_mutex); crfree(of->f_cr); smb_user_release(of->f_user); kmem_cache_free(smb_cache_ofile, of); }
/* * smb_ofile_open */ smb_ofile_t * smb_ofile_open( smb_tree_t *tree, smb_node_t *node, uint16_t pid, struct open_param *op, uint16_t ftype, uint32_t uniqid, smb_error_t *err) { smb_ofile_t *of; uint16_t fid; smb_attr_t attr; int rc; if (smb_idpool_alloc(&tree->t_fid_pool, &fid)) { err->status = NT_STATUS_TOO_MANY_OPENED_FILES; err->errcls = ERRDOS; err->errcode = ERROR_TOO_MANY_OPEN_FILES; return (NULL); } of = kmem_cache_alloc(tree->t_server->si_cache_ofile, KM_SLEEP); bzero(of, sizeof (smb_ofile_t)); of->f_magic = SMB_OFILE_MAGIC; of->f_refcnt = 1; of->f_fid = fid; of->f_uniqid = uniqid; of->f_opened_by_pid = 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(tree->t_user) : tree->t_user->u_cred; crhold(of->f_cr); of->f_ftype = ftype; of->f_server = tree->t_server; of->f_session = tree->t_user->u_session; of->f_user = tree->t_user; of->f_tree = tree; of->f_node = node; mutex_init(&of->f_mutex, NULL, MUTEX_DEFAULT, NULL); of->f_state = SMB_OFILE_STATE_OPEN; if (ftype == SMB_FTYPE_MESG_PIPE) { of->f_pipe = smb_opipe_alloc(tree->t_server); smb_server_inc_pipes(of->f_server); } else { ASSERT(ftype == SMB_FTYPE_DISK); /* Regular file, not a pipe */ ASSERT(node); if (of->f_granted_access == FILE_EXECUTE) of->f_flags |= SMB_OFLAGS_EXECONLY; bzero(&attr, sizeof (smb_attr_t)); attr.sa_mask = SMB_AT_UID | SMB_AT_DOSATTR; rc = smb_node_getattr(NULL, node, of->f_cr, NULL, &attr); if (rc != 0) { of->f_magic = 0; mutex_destroy(&of->f_mutex); crfree(of->f_cr); smb_idpool_free(&tree->t_fid_pool, of->f_fid); kmem_cache_free(tree->t_server->si_cache_ofile, of); err->status = NT_STATUS_INTERNAL_ERROR; err->errcls = ERRDOS; err->errcode = ERROR_INTERNAL_ERROR; return (NULL); } if (crgetuid(of->f_cr) == attr.sa_vattr.va_uid) { /* * Add this bit for the file's owner even if it's not * specified in the request (Windows behavior). */ of->f_granted_access |= FILE_READ_ATTRIBUTES; } if (smb_node_is_file(node)) { of->f_mode = smb_fsop_amask_to_omode(of->f_granted_access); if (smb_fsop_open(node, of->f_mode, of->f_cr) != 0) { of->f_magic = 0; mutex_destroy(&of->f_mutex); crfree(of->f_cr); smb_idpool_free(&tree->t_fid_pool, of->f_fid); kmem_cache_free(tree->t_server->si_cache_ofile, of); err->status = NT_STATUS_ACCESS_DENIED; err->errcls = ERRDOS; err->errcode = ERROR_ACCESS_DENIED; return (NULL); } } if (tree->t_flags & SMB_TREE_READONLY) of->f_flags |= SMB_OFLAGS_READONLY; /* * Note that if we created_readonly, that * will _not_ yet show in attr.sa_dosattr * so creating a readonly file gives the * caller a writable handle as it should. */ if (attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) of->f_flags |= SMB_OFLAGS_READONLY; smb_node_inc_open_ofiles(node); smb_node_add_ofile(node, of); smb_node_ref(node); smb_server_inc_files(of->f_server); } smb_llist_enter(&tree->t_ofile_list, RW_WRITER); smb_llist_insert_tail(&tree->t_ofile_list, of); smb_llist_exit(&tree->t_ofile_list); atomic_inc_32(&tree->t_open_files); atomic_inc_32(&of->f_session->s_file_cnt); return (of); }
/* * smb_ofile_open */ smb_ofile_t * smb_ofile_open( smb_request_t *sr, smb_node_t *node, struct open_param *op, uint16_t ftype, uint32_t uniqid, smb_error_t *err) { smb_tree_t *tree = sr->tid_tree; smb_ofile_t *of; uint16_t fid; smb_attr_t attr; int rc; enum errstates { EMPTY, FIDALLOC, CRHELD, MUTEXINIT }; enum errstates state = EMPTY; if (smb_idpool_alloc(&tree->t_fid_pool, &fid)) { err->status = NT_STATUS_TOO_MANY_OPENED_FILES; err->errcls = ERRDOS; err->errcode = ERROR_TOO_MANY_OPEN_FILES; return (NULL); } state = FIDALLOC; of = kmem_cache_alloc(smb_cache_ofile, KM_SLEEP); bzero(of, sizeof (smb_ofile_t)); of->f_magic = SMB_OFILE_MAGIC; of->f_refcnt = 1; of->f_fid = fid; of->f_uniqid = uniqid; 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); state = CRHELD; of->f_ftype = ftype; of->f_server = tree->t_server; of->f_session = tree->t_session; /* * grab a ref for of->f_user * released in smb_ofile_delete() */ smb_user_hold_internal(sr->uid_user); of->f_user = sr->uid_user; of->f_tree = tree; of->f_node = node; mutex_init(&of->f_mutex, NULL, MUTEX_DEFAULT, NULL); state = MUTEXINIT; of->f_state = SMB_OFILE_STATE_OPEN; if (ftype == SMB_FTYPE_MESG_PIPE) { /* See smb_opipe_open. */ of->f_pipe = op->pipe; smb_server_inc_pipes(of->f_server); } else { ASSERT(ftype == SMB_FTYPE_DISK); /* Regular file, not a pipe */ ASSERT(node); /* * Note that the common open path often adds bits like * READ_CONTROL, so the logic "is this open exec-only" * needs to look at only the FILE_DATA_ALL bits. */ if ((of->f_granted_access & FILE_DATA_ALL) == FILE_EXECUTE) of->f_flags |= SMB_OFLAGS_EXECONLY; bzero(&attr, sizeof (smb_attr_t)); attr.sa_mask = SMB_AT_UID | SMB_AT_DOSATTR; rc = smb_node_getattr(NULL, node, of->f_cr, NULL, &attr); if (rc != 0) { err->status = NT_STATUS_INTERNAL_ERROR; err->errcls = ERRDOS; err->errcode = ERROR_INTERNAL_ERROR; goto errout; } if (crgetuid(of->f_cr) == attr.sa_vattr.va_uid) { /* * Add this bit for the file's owner even if it's not * specified in the request (Windows behavior). */ of->f_granted_access |= FILE_READ_ATTRIBUTES; } if (smb_node_is_file(node)) { of->f_mode = smb_fsop_amask_to_omode(of->f_granted_access); if (smb_fsop_open(node, of->f_mode, of->f_cr) != 0) { err->status = NT_STATUS_ACCESS_DENIED; err->errcls = ERRDOS; err->errcode = ERROR_ACCESS_DENIED; goto errout; } } if (tree->t_flags & SMB_TREE_READONLY) of->f_flags |= SMB_OFLAGS_READONLY; /* * Note that if we created_readonly, that * will _not_ yet show in attr.sa_dosattr * so creating a readonly file gives the * caller a writable handle as it should. */ if (attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) of->f_flags |= SMB_OFLAGS_READONLY; smb_node_inc_open_ofiles(node); smb_node_add_ofile(node, of); smb_node_ref(node); smb_server_inc_files(of->f_server); } smb_llist_enter(&tree->t_ofile_list, RW_WRITER); smb_llist_insert_tail(&tree->t_ofile_list, of); smb_llist_exit(&tree->t_ofile_list); atomic_inc_32(&tree->t_open_files); atomic_inc_32(&of->f_session->s_file_cnt); return (of); errout: switch (state) { case MUTEXINIT: mutex_destroy(&of->f_mutex); smb_user_release(of->f_user); /*FALLTHROUGH*/ case CRHELD: crfree(of->f_cr); of->f_magic = 0; kmem_cache_free(smb_cache_ofile, of); /*FALLTHROUGH*/ case FIDALLOC: smb_idpool_free(&tree->t_fid_pool, fid); /*FALLTHROUGH*/ case EMPTY: break; } return (NULL); }
/* * Allocate a tree. */ static smb_tree_t * smb_tree_alloc( smb_user_t *user, const smb_share_t *si, int32_t stype, smb_node_t *snode, uint32_t access) { smb_tree_t *tree; uint16_t tid; if (smb_idpool_alloc(&user->u_tid_pool, &tid)) return (NULL); tree = kmem_cache_alloc(user->u_server->si_cache_tree, KM_SLEEP); bzero(tree, sizeof (smb_tree_t)); if (STYPE_ISDSK(stype)) { if (smb_tree_getattr(si, snode, tree) != 0) { smb_idpool_free(&user->u_tid_pool, tid); kmem_cache_free(user->u_server->si_cache_tree, tree); return (NULL); } } if (smb_idpool_constructor(&tree->t_fid_pool)) { smb_idpool_free(&user->u_tid_pool, tid); kmem_cache_free(user->u_server->si_cache_tree, tree); return (NULL); } if (smb_idpool_constructor(&tree->t_odid_pool)) { smb_idpool_destructor(&tree->t_fid_pool); smb_idpool_free(&user->u_tid_pool, tid); kmem_cache_free(user->u_server->si_cache_tree, tree); return (NULL); } smb_llist_constructor(&tree->t_ofile_list, sizeof (smb_ofile_t), offsetof(smb_ofile_t, f_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_user = user; tree->t_session = user->u_session; tree->t_server = user->u_server; 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(); /* if FS is readonly, enforce that here */ if (tree->t_flags & SMB_TREE_READONLY) tree->t_access &= ~ACE_ALL_WRITE_PERMS; if (STYPE_ISDSK(stype)) { smb_node_ref(snode); tree->t_snode = snode; tree->t_acltype = smb_fsop_acltype(snode); } smb_llist_enter(&user->u_tree_list, RW_WRITER); smb_llist_insert_head(&user->u_tree_list, tree); smb_llist_exit(&user->u_tree_list); atomic_inc_32(&user->u_session->s_tree_cnt); atomic_inc_32(&user->u_server->sv_open_trees); return (tree); }