smb_tree.c revision b7301bf5522d8b9141fe432333ded586218327f2
/*
* 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
* 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 2011 Nexenta Systems, Inc. All rights reserved.
*/
/*
* General Structures Layout
* -------------------------
*
* This is a simplified diagram showing the relationship between most of the
* main structures.
*
* +-------------------+
* | SMB_INFO |
* +-------------------+
* |
* |
* v
* +-------------------+ +-------------------+ +-------------------+
* | SESSION |<----->| SESSION |......| SESSION |
* +-------------------+ +-------------------+ +-------------------+
* |
* |
* v
* +-------------------+ +-------------------+ +-------------------+
* | USER |<----->| USER |......| USER |
* +-------------------+ +-------------------+ +-------------------+
* |
* |
* v
* +-------------------+ +-------------------+ +-------------------+
* | TREE |<----->| TREE |......| TREE |
* +-------------------+ +-------------------+ +-------------------+
* | |
* | |
* | v
* | +-------+ +-------+ +-------+
* | | OFILE |<----->| OFILE |......| OFILE |
* | +-------+ +-------+ +-------+
* |
* |
* v
* +-------+ +------+ +------+
* | ODIR |<----->| ODIR |......| ODIR |
* +-------+ +------+ +------+
*
*
* Tree State Machine
* ------------------
*
* +-----------------------------+ T0
* | SMB_TREE_STATE_CONNECTED |<----------- Creation/Allocation
* +-----------------------------+
* |
* | T1
* |
* v
* +------------------------------+
* | SMB_TREE_STATE_DISCONNECTING |
* +------------------------------+
* |
* | T2
* |
* v
* +-----------------------------+ T3
* +-----------------------------+
*
* SMB_TREE_STATE_CONNECTED
*
* While in this state:
* - The tree is queued in the list of trees of its user.
* - References will be given out if the tree is looked up.
* - Files under that tree can be accessed.
*
* SMB_TREE_STATE_DISCONNECTING
*
* While in this state:
* - The tree is queued in the list of trees of its user.
* - References will not be given out if the tree is looked up.
* - The files and directories open under the tree are being closed.
* - The resources associated with the tree remain.
*
* SMB_TREE_STATE_DISCONNECTED
*
* While in this state:
* - The tree is queued in the list of trees of its user.
* - References will not be given out if the tree is looked up.
* - The tree has no more files and directories opened.
* - The resources associated with the tree remain.
*
* Transition T0
*
* This transition occurs in smb_tree_connect(). A new tree is created and
* added to the list of trees of a user.
*
* Transition T1
*
* This transition occurs in smb_tree_disconnect().
*
* Transition T2
*
* This transition occurs in smb_tree_release(). The resources associated
* with the tree are freed as well as the tree structure. For the transition
* to occur, the tree must be in the SMB_TREE_STATE_DISCONNECTED state and
* the reference count be zero.
*
* Comments
* --------
*
* The state machine of the tree structures is controlled by 3 elements:
* - The list of trees of the user it belongs to.
* - The mutex embedded in the structure itself.
* - The reference count.
*
* There's a mutex embedded in the tree structure used to protect its fields
* and there's a lock embedded in the list of trees of a user. To
* increment or to decrement the reference count the mutex must be entered.
* To insert the tree into the list of trees of the user and to remove
* the tree from it, the lock must be entered in RW_WRITER mode.
*
* Rules of access to a tree structure:
*
* 1) In order to avoid deadlocks, when both (mutex and lock of the user
* list) have to be entered, the lock must be entered first.
*
* 2) All actions applied to a tree require a reference count.
*
* 3) There are 2 ways of getting a reference count: when a tree is
* connected and when a tree is looked up.
*
* It should be noted that the reference count of a tree registers the
* number of references to the tree in other structures (such as an smb
* request). The reference count is not incremented in these 2 instances:
*
* 1) The tree is connected. An tree is anchored by his state. If there's
* no activity involving a tree currently connected, the reference
* count of that tree is zero.
*
* 2) The tree is queued in the list of trees of the user. The fact of
* being queued in that list is NOT registered by incrementing the
* reference count.
*/
#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 = 0;
static const char *smb_tree_get_sharename(const char *);
static void smb_tree_log(smb_request_t *, const char *, const char *, ...);
static void smb_tree_netinfo_fini(smb_netconnectinfo_t *);
{
return (NULL);
}
return (tree);
}
/*
* Lookup the share name dispatch the appropriate stype handler.
* Share names are case insensitive so we map the share name to
* lower-case as a convenience for internal processing.
*
* Valid service values are:
* A: Disk share
* LPT1: Printer
* IPC Named pipe (IPC$ is reserved as the named pipe share).
* COMM Communications device
* ????? Any type of device (wildcard)
*/
static smb_tree_t *
{
const char *name;
(void) smb_strlwr(unc_path);
return (NULL);
}
return (NULL);
}
return (NULL);
}
case STYPE_DISKTREE:
break;
case STYPE_IPC:
break;
case STYPE_PRINTQ:
break;
default:
break;
}
return (tree);
}
/*
* Disconnect a tree.
*/
void
{
if (smb_tree_is_connected_locked(tree)) {
/*
* Indicate that the disconnect process has started.
*/
if (do_exec) {
/*
* The files opened under this tree are closed.
*/
/*
* The directories opened under this tree are closed.
*/
smb_tree_close_odirs(tree, 0);
}
}
(void) smb_kshare_exec(&execinfo);
}
}
/*
* Take a reference on a tree.
*/
{
if (smb_tree_is_connected_locked(tree)) {
return (B_TRUE);
}
return (B_FALSE);
}
/*
* Release a reference on a tree. If the tree is disconnected and the
* reference count falls to zero, post the object for deletion.
* Object deletion is deferred to avoid modifying a list while an
* iteration may be in progress.
*/
void
{
/* flush the ofile and odir lists' delete queues */
}
void
{
}
void
{
}
/*
* Close ofiles and odirs that match pid.
*/
void
{
}
/*
* Check whether or not a tree supports the features identified by flags.
*/
{
}
/*
* If the enumeration request is for tree data, handle the request
* here. Otherwise, pass it on to the ofiles.
*
* This function should be called with a hold on the tree.
*/
int
{
int rc;
while (of) {
if (rc != 0) {
break;
}
}
return (rc);
}
/*
* Close a file by its unique id.
*/
int
{
return (ENOENT);
if (smb_ofile_disallow_fclose(of)) {
return (EACCES);
}
smb_ofile_close(of, 0);
return (0);
}
/* *************************** Static Functions ***************************** */
#define SHARES_DIR ".zfs/shares/"
/*
* Calculates permissions given by the share's ACL to the
* user in the passed request. The default is full access.
* If any error occurs, full access is granted.
*
* Using the vnode of the share path find the root directory
* of the mounted file system. Then look to see if there is a
* the same name as the share name in it. The ACL set for this
* file is the share's ACL which is used for access check here.
*/
static uint32_t
{
int rc;
char *sharepath;
/*
* An autohome share owner gets full access to the share.
* Everyone else is denied access.
*/
access = 0;
return (access);
}
/*
* The hold on 'root' is released by the lookuppnvp() that follows
*/
else
if (rc != 0)
return (access);
kcred);
/*
* Now get the effective access value based on cred and ACL values.
*/
if (rc == 0) {
cred);
}
return (access);
}
/*
* Performs the following access checks for a disk share:
*
*
* - If user is Guest, guestok property of the share should be
* enabled
*
* - If this is an Admin share, the user should have administrative
* privileges
*
* - Host based access control lists
*
* - Share ACL
*
* Returns the access allowed or 0 if access is denied.
*/
static uint32_t
{
return (0);
}
return (0);
}
return (0);
}
if ((host_access & ACE_ALL_PERMS) == 0) {
return (0);
}
if ((acl_access & ACE_ALL_PERMS) == 0) {
return (0);
}
if ((access & ACE_ALL_PERMS) == 0) {
return (0);
}
return (access);
}
/*
* Connect a share for use with files and directories.
*/
static smb_tree_t *
{
const char *any = "?????";
char last_component[MAXNAMELEN];
int rc;
return (NULL);
}
/*
* Check that the shared directory exists.
*/
if (rc == 0) {
&snode);
}
if (rc) {
if (snode)
return (NULL);
}
return (NULL);
}
/*
* Set up the OptionalSupport for this share.
*/
case SMB_SHRF_CSC_DISABLED:
break;
case SMB_SHRF_CSC_AUTO:
break;
case SMB_SHRF_CSC_VDO:
break;
case SMB_SHRF_CSC_MANUAL:
default:
/*
* Default to SMB_CSC_CACHE_MANUAL_REINT.
*/
break;
}
/* ABE support */
/* if 'smb' zfs property: shortnames=disabled */
if (!smb_shortnames)
if (tree) {
return (NULL);
}
}
} else {
}
return (tree);
}
/*
* Shares have both a share and host based access control. The access
* granted will be minimum permissions based on both hostaccess
* (permissions allowed by host based access) and aclaccess (from the
* share ACL).
*/
static smb_tree_t *
{
const char *any = "?????";
char last_component[MAXNAMELEN];
int rc;
return (NULL);
}
return (NULL);
}
/*
* Check that the shared directory exists.
*/
if (rc == 0) {
&snode);
}
if (rc) {
if (snode)
return (NULL);
}
return (NULL);
}
return (tree);
}
/*
* Connect an IPC share for use with named pipes.
*/
static smb_tree_t *
{
const char *any = "?????";
return (NULL);
}
return (NULL);
}
}
return (tree);
}
/*
* Allocate a tree.
*/
static smb_tree_t *
{
return (NULL);
return (NULL);
}
}
return (NULL);
}
return (NULL);
}
sizeof (tree->t_sharename));
sizeof (tree->t_resource));
/* if FS is readonly, enforce that here */
}
return (tree);
}
/*
* Deallocate a tree. The open file and open directory lists should be
* empty.
*
* Remove the tree from the user's tree list before freeing resources
* associated with the tree.
*/
void
smb_tree_dealloc(void *arg)
{
}
/*
* Determine whether or not a tree is connected.
* This function must be called with the tree mutex held.
*/
static boolean_t
{
case SMB_TREE_STATE_CONNECTED:
return (B_TRUE);
/*
* The tree exists but being diconnected or destroyed.
*/
return (B_FALSE);
default:
ASSERT(0);
return (B_FALSE);
}
}
/*
* Determine whether or not a tree is disconnected.
* This function must be called with the tree mutex held.
*/
static boolean_t
{
return (B_TRUE);
case SMB_TREE_STATE_CONNECTED:
return (B_FALSE);
default:
ASSERT(0);
return (B_FALSE);
}
}
/*
* Return a pointer to the share name within a share resource path.
*
* The share path may be a Uniform Naming Convention (UNC) string
* (\\server\share) or simply the share name. We validate the UNC
* format but we don't look at the server name.
*/
static const char *
smb_tree_get_sharename(const char *unc_path)
{
if (sharename[0] == '\\') {
/*
* Looks like a UNC path, validate the format.
*/
return (NULL);
return (NULL);
++sharename;
/*
* This should be a share name (no embedded \'s).
*/
return (NULL);
}
return (sharename);
}
/*
* Obtain the tree attributes: volume name, typename and flags.
*/
static int
{
return (ESTALE);
return (0);
}
/*
* Extract the volume name.
*/
static void
{
const char *s;
char *name;
s = vfs_mntpoint->rs_string;
s += strspn(s, "/");
}
/*
* Always set ACL support because the VFS will fake ACLs for file systems
* that don't support them.
*
* Some flags are dependent on the typename, which is also set up here.
* File system types are hardcoded in uts/common/os/vfs_conf.c.
*/
static void
{
typedef struct smb_mtype {
char *mt_name;
} smb_mtype_t;
static smb_mtype_t smb_mtype[] = {
};
char *name;
int i;
flags |= SMB_TREE_CATIA;
flags |= SMB_TREE_ABE;
/* if 'smb' zfs property: oplocks=enabled */
}
/* if 'smb' zfs property: shortnames=enabled */
if (smb_shortnames)
}
flags |= SMB_TREE_XVATTR;
}
/*
* Report share access result to syslog.
*/
static void
{
char buf[128];
if (smb_tcon_mute)
return;
/*
* Only report normal users, i.e. ignore W2K misuse
* of the IPC connection by filtering out internal
* names such as nobody and root.
*/
return;
}
}
}
/*
* smb_tree_lookup_odir
*
* Find the specified odir in the tree's list of odirs, and
* attempt to obtain a hold on the odir.
*
* Returns NULL if odir not found or a hold cannot be obtained.
*/
{
smb_odir_t *od;
while (od) {
if (!smb_odir_hold(od))
break;
}
}
return (od);
}
{
return (rb);
}
/*
* 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 *
{
if (of) {
} else {
}
while (of) {
if (smb_ofile_hold(of))
break;
}
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 *
{
if (od) {
} else {
}
while (od) {
if (smb_odir_hold(od))
break;
}
return (od);
}
/*
* smb_tree_close_odirs
*
* Close all open odirs in the tree's list which were opened by
* the process identified by pid.
* If pid is zero, close all open odirs in the tree's list.
*/
static void
{
while (od) {
}
}
static void
{
}
/*
* Private function to support smb_tree_enum.
*/
static int
{
int rc;
return (0);
}
return (0);
}
if (rc == 0) {
}
return (rc);
}
/*
* Encode connection information into a buffer: connection information
* needed in user space to support RPC requests.
*/
static int
{
int rc;
return (rc);
}
/*
* Note: ci_numusers should be the number of users connected to
* the share rather than the number of references on the tree but
*/
static void
{
}
static void
{
return;
if (info->ci_username)
}