mgmt_create.c revision a9fd9a9e12bea66c9ea9b31f4b6f0ef584933f59
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <ctype.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <strings.h>
#include <sys/efi_partition.h>
#include <libzfs.h>
#include <syslog.h>
#include <priv.h>
#include <iscsitgt_impl.h>
#include "queue.h"
#include "target.h"
#include "iscsi_cmd.h"
#include "utility.h"
#include "errcode.h"
#include "t10_spc.h"
#include "isns_client.h"
#include "mgmt_scf.h"
extern char *getfullrawname();
static char *create_target(tgt_node_t *);
static char *create_initiator(tgt_node_t *);
static char *create_tpgt(tgt_node_t *);
/*
* []----
* | create_func -- Branch out to appropriate object create function
* []----
*/
/*ARGSUSED*/
void
{
tgt_node_t *x;
char msgbuf[80];
} else {
x = p->x_child;
reply_msg = create_target(x);
reply_msg = create_initiator(x);
reply_msg = create_tpgt(x);
} else {
"Unknown object '%s' for create element",
x->x_name);
}
}
}
/*
* create_target -- an administrative request to create a target
*/
static char *
{
char path[MAXPATHLEN];
int lun = 0; /* default to LUN 0 */
int i;
tgt_node_t *n, *c, *l;
goto error;
}
/*
* We've got to have a name element or all bets are off.
*/
goto error;
}
/*
* RFC3722 states that names must be one of:
* (1) a..z
* (2) A..Z
* (3) 0-9
* or
* (4) ':', '.', '-'
* If it's an upper case character is must be made lower
* case.
*/
goto error;
}
/*
* If a type hasn't been specified default to disk emulation.
* We use strdup() since at the end of this routine the code
* is expecting to free 'type' along with other strings.
*/
}
goto error;
}
/*
* If a backing store has been provided we don't
* need the size since we can determine that from
* a READ_CAPACITY command which everyone issues.
*
* NOTE: strdup is used here, since at the end
* of this routine any of the string pointers which
* are non-NULL get freed.
*/
} else {
goto error;
}
}
goto error;
}
/*
* See if we already have a local target name created. If so,
* the user is most likely wanting to create another LUN for this
* target. Checking to see if there's a duplicate LUN will be
* done later.
*/
break;
}
if (n == NULL) {
if (lun != 0) {
goto error;
}
goto error;
}
goto error;
}
tgt_node_add(n, c);
tgt_node_add(c, l);
tgt_node_add(n, c);
tgt_node_add(n, c);
}
} else {
if (tgt_find_value_str(n, XML_ELEMENT_INAME,
goto error;
}
goto error;
}
tgt_node_add(c, l);
}
== True) {
if (mgmt_config_save2scf() == False)
goto error;
/* Only isns register on the 1st creation of the target */
goto error;
}
}
/*
* The first LU will have created the directory and
* symbolic link. Remove those on error.
*/
} else
tgt_node_remove(c, l, MatchBoth);
return (msg);
}
static char *
{
char *iscsi_name = NULL;
tgt_node_t *n, *c;
goto error;
}
goto error;
}
goto error;
}
goto error;
}
}
tgt_node_add(n, c);
tgt_node_add(main_config, n);
if (mgmt_config_save2scf() == True)
if (name)
if (iscsi_name)
return (msg);
}
static char *
{
tgt_node_t *n;
int tpgt_val;
goto error;
}
/* ---- Validation checks ---- */
goto error;
}
goto error;
}
}
tgt_node_add(main_config, n);
if (mgmt_config_save2scf() == True)
if (tpgt)
return (msg);
}
/*
* create_zfs -- given a dataset, export it through the iSCSI protocol
*
* This function is called when someone uses the libiscsitgt function
* iscsitgt_zfs_share(char *dataset)
*/
static char *
{
char path[MAXPATHLEN];
char *cp; /* current pair */
char *np; /* next pair */
char *vp; /* value pointer */
tgt_node_t *n;
tgt_node_t *c;
tgt_node_t *ll;
tgt_node_t *l;
int lun = 0;
const priv_set_t *eset;
goto error;
}
goto error;
}
ucred_geteuid(cred) != 0) {
/*
* See if user has ZFS dataset permissions to do operation
*/
goto error;
}
}
goto error;
}
}
prop_len = 1024;
goto error;
}
/*
* In case zfs_prop_get returns no contents.
*/
*prop = '\0';
/*
* If we fail to get the shareiscsi property or the property is
* set to "off" return an error.
*/
goto error;
}
/*
* by comma characters. Stand alone values of 'on' and 'off' are
* also permitted, but having the property set to off when share()
* is called is an error.
* Currently we only look for 'type=<value>' and ignore others.
*/
while (cp) {
*np++ = '\0';
continue;
}
goto error;
}
*vp++ = '\0';
/*
* Only support 'disk' emulation at this point.
*/
goto error;
}
}
/*
* In case zfs_prop_get returns no contents.
*/
*prop = '\0';
goto error;
}
/*
* Pick up the current size of the volume.
*/
n = NULL;
while (xmlTextReaderRead(r)) {
if (tgt_node_process(r, &n) == False)
break;
}
/*
* Pick up the LU node from the LU List which hangs
* off of the target node. If either is NULL there's
* an internal error someplace.
* 'l' will be used later.
*/
== NULL) ||
NULL)) {
goto error;
}
} else {
goto error;
}
} else {
goto error;
}
tgt_node_add_attr(n, attr);
tgt_node_add_attr(n, attr);
tgt_node_add(n, c);
tgt_node_add(n, c);
tgt_node_add(c, l);
tgt_node_add_attr(l, attr);
/*
* Now store this information on the ZVOL property so that
* next time we get a share request the same data will be
* used.
*/
tgt_dump2buf(n, &prop);
prop)) {
goto error;
}
}
/*
* NOTE: 'n' is expected to be the target node for this
* ZVOL at this point.
*/
/*
* Several elements can change everytime we share
* the dataset. The TargetAlias and backing store
* can change since these are based on the dataset
* and the size of the volume. Therefore, these
* pieces of information are not stored as part
* of the iscsioptions properity.
*/
tgt_node_add(n, c);
tgt_node_add(l, c);
size /= 512LL;
tgt_node_add(l, c);
/* register with iSNS */
if (isns_enabled() == True) {
goto error;
}
}
if (zfsh)
if (zh)
if (dataset)
if (node_name)
if (prop)
return (msg);
}
/*
* []------------------------------------------------------------------[]
* | Utility functions used by the routines above. |
* []------------------------------------------------------------------[]
*/
/*
* []----
* | create_node_name -- Creates the IQN that adhears to RFC3270
* []----
*/
static char *
{
char id_str[37];
char *p;
char *anp; /* alias or nick pointer */
return (NULL);
/*
* Originally we we going to use the machines MAC address and
* timestamp in hex format. This would be consistent with the
* Solaris iSCSI initiator and NAS5310. Unfortunately, someone
* pointed out that there's no requirement that the network
* interfaces be plumbed before someone attempts to create
* targets. If the networks aren't plumbed there are no MAC
* addresses available and we can't use 0 for the MAC address
* since that would introduce the probability of non-unique
* IQN names.
*/
free(p);
return (NULL);
}
if (local_nick || alias) {
/*
* Make sure we still have room to add the alias or
* local nickname, a '.', and NULL character to the
* buffer.
*/
free(p);
return (NULL);
}
(void) strcat(p, ".");
}
return (p);
}
/*
* []----
* | create_target_dir -- create the target directory
* []----
*/
static Boolean_t
{
char path[MAXPATHLEN];
char sympath[MAXPATHLEN];
return (False);
return (False);
/*
* This symbolic link is here for convenience and nothing more, so if
* if fails. Oh well.
*/
return (True);
}
/*
* []----
* | create_lun -- given type, lun, size, backing create LU and params
* []----
*/
static Boolean_t
{
int fd = -1;
int rpm = DEFAULT_RPM;
int heads = DEFAULT_HEADS;
int cylinders = DEFAULT_CYLINDERS;
int spt = DEFAULT_SPT;
int bytes_sect = DEFAULT_BYTES_PER;
int interleave = DEFAULT_INTERLEAVE;
char *vid = DEFAULT_VID;
char *pid = DEFAULT_PID;
char path[MAXPATHLEN];
tgt_node_t *n = NULL;
/*
* after calling stroll_multipler it's an error for size to be
* 0, if and only if, the size_str doesn't equal "0". The administrator
* may want the code to determine the size. This would be the case
* when the administrator has provide a backing store which exists.
*/
*code = ERR_INVALID_SIZE;
return (False);
}
if ((size % 512) != 0) {
return (False);
}
/*
* Make sure we're not trying to recreate an existing LU.
*/
*code = ERR_LUN_EXISTS;
return (False);
}
tgt_node_add_attr(n, pn);
tgt_node_add(n, pn);
tgt_node_add(n, pn);
tgt_node_add(n, pn);
tgt_node_add(n, pn);
goto error;
tgt_node_add(n, pn);
tgt_node_add(n, pn);
tgt_node_add(n, pn);
tgt_node_add(n, pn);
tgt_node_add(n, pn);
tgt_node_add(n, pn);
tgt_node_add(n, pn);
#ifndef _LP64
goto error;
#else
tgt_node_add(n, pn);
goto error;
#endif
tgt_node_add(n, pn);
goto error;
tgt_node_add(n, pn);
goto error;
}
goto error;
}
/*
* Wait to set the size until here because it may be unknown until
* the possible backing store has been setup.
*/
tgt_node_add(n, pn);
tgt_node_add(n, pn);
}
goto error;
}
tgt_node_free(n);
*code = ERR_SUCCESS;
return (True);
/* Free node n */
tgt_node_free(n);
if (fd == -1)
return (False);
}
/*
* []----
* | create_lun_common -- create LU and start provisioning if needed
* |
* | This function is common to both the tape and disk emulation
* | code.
* []----
*/
static Boolean_t
{
struct stat s;
int fd = -1;
char path[MAXPATHLEN];
char buf[512];
tgt_node_t *c;
/*
* Touch the last block of the file which will cause file systems
* to understand the intent of the file to be a certain size. The
* space isn't allocated, but the daemon can then mmap in this file
* and start writing to it.
*/
goto error;
*code = ERR_FILE_TO_BIG;
else
goto error;
}
/*
* Set the fd back to -1 so that if an error occurs we don't
* attempt to close this device twice. This could be an issue
* if another thread opened a file right after we closed this
* one and the system reused the file descriptor. During an
* error we would then close another threads file which would
* be ugly, not to mention difficult to track down.
*/
fd = -1;
goto error;
}
/*
* If the backing store is a regular file and the default is
* used which initializes the file instead of sparse allocation
* go ahead a set things up.
*/
/*
* Attempt to see if there is enough space currently
* for the LU. The initialization might still fail
* with "out of space" because someone else is
* consuming space while the initialization is occuring.
* Nothing we can do about that.
*/
"GEN statvfs failed for %s", path);
goto error;
"GEN Not enough space for LU");
*code = ERR_FILE_TO_BIG;
goto error;
}
/*
* Run the initialization thread in the background so that
* the administrator doesn't have to wait which for UFS could
* be a long time on a large LU.
*/
tp->q = queue_alloc();
/*
* As soon as the thread starts it will send a simple
* ACK to it's own queue that we can look for. When
* we see this message we know that the thread has
* started and it's been added to the provisioning
* list. If this were not done it's possible for someone
* to create and delete a target within a script and
* have the delete run and fail to find the provision
* thread in the list.
*/
}
} else {
tgt_node_free(c);
"GEN%d failed to dump out params", lun);
goto error;
}
}
return (True);
if (fd != -1)
return (False);
}
static Boolean_t
{
return (True);
else
return (False);
}
static Boolean_t
{
return (True);
else
return (False);
}
/*
* []----
* | setup_alt_backing -- use backing store link for regular file lun
* |
* | If the size is zero, then the administrator MUST have
* | specified a backing store to use.
* | If the size is non-zero and the backing store doesn't exist it will
* | be created. Also a tag will be added indicating that during removal
* | the backing store should be deleted as well.
* []----
*/
static Boolean_t
{
struct stat s;
tgt_node_t *pn;
/*
* Error checking regarding size and backing store has already
* been done. If the backing store is null at this point everything
* is okay so just return True.
*/
return (True);
if (*size == 0) {
return (False);
} else {
"true");
tgt_node_add(n, pn);
0600)) < 0) {
return (False);
}
}
} else if (*size != 0) {
return (False);
return (False);
}
} else {
return (False);
}
} else {
return (False);
}
return (False);
}
return (True);
}
/*
* []----
* | validate_raw_backing -- check that device is full partition
* |
* | The size of the device will be returned in rtn_size in bytes.
* |
* | Need to guarantee that the backing store for a raw device is:
* | (a) character device
* | (b) Not buffered
* | Don't want this host to have data which is not flushed
* | out during a write since a multiple path access to
* | the backing store would be possible meaning we'd have
* | cache issue.
* | To speed things up we use asynchronous I/O which means
* | the path has to have access to the entire device through
* | the partition table. If not, some client will issue a
* | READ_CAPACITY command, but not be able to access all of
* | the data.
* []----
*/
static Boolean_t
{
struct stat s;
char buf[512];
int fd;
struct uscsi_cmd u;
struct scsi_extended_sense sense;
struct scsi_capacity cap;
struct scsi_capacity_16 cap16;
return (False);
return (False);
}
return (False);
}
bzero(&u, sizeof (u));
u.uscsi_cdblen = CDB_GROUP1;
u.uscsi_buflen = sizeof (cap);
u.uscsi_rqbuf = (char *)&sense;
u.uscsi_rqlen = sizeof (sense);
goto error;
}
bzero(&u, sizeof (u));
/*
* The device is to large for the 10byte CDB.
* Using the larger 16byte read capacity command
*/
u.uscsi_cdblen = CDB_GROUP4;
u.uscsi_buflen = sizeof (cap16);
u.uscsi_rqbuf = (char *)&sense;
u.uscsi_rqlen = sizeof (sense);
"GEN0 uscsi(READ_CAP16) failed");
goto error;
}
} else
"GEN0 Partition size != capacity(0x%llx), cc=%d, errno=%d",
goto error;
} else {
}
return (rval);
}
static void
{
tgt_node_t *c;
guid = 0;
tgt_node_add(l, c);
tgt_node_add(l, c);
tgt_node_add(l, c);
tgt_node_add(l, c);
val = DEFAULT_RPM;
tgt_node_add(l, c);
tgt_node_add(l, c);
tgt_node_add(l, c);
tgt_node_add(l, c);
tgt_node_add(l, c);
tgt_node_add(l, c);
tgt_node_add(l, c);
}