/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <string.h>
#include <libintl.h>
#include "metassist.h"
#include "volume_dlist.h"
#include "volume_error.h"
#include "volume_string.h"
#include "volume_output.h"
#define _LAYOUT_VALIDATE_C
#include "layout_discovery.h"
#include "layout_dlist_util.h"
#include "layout_device_cache.h"
#include "layout_device_util.h"
#include "layout_request.h"
#include "layout_slice.h"
#include "layout_svm_util.h"
#include "layout_validate.h"
/*
* This module contains the majority of the validation code which
* layout applies to input requests. The assumption/agreement with
* the controller implementation is that requests passed into layout
* have undergone syntactic validation and that layout is responsible
* for semantic validation.
*
* The semantic validation that is handled:
*
* 1. For a toplevel diskset request, validate:
*
* - the number of disksets is not exceeded
* - the number of devices is not exceeded
*
* (These items are not directly validated within this module,
* but it is useful to document that they are handled somewhere).
*
* 2. For any devconfig_t representing a volume request, verify that:
*
* - all HSP names are semantically valid. The name should conform
* to the HSP naming convention: hspXXX.
*
* - all concat, stripe, mirror, and volume names refer to
* unused, semantically valid metadevice names. Examples of
* bad data:
*
* - a valid volume name that is already in use (d0, d10)
*
* - a valid volume name that is used two or more times to
* refer to new elements in the request.
*
* - a valid volume name that is out of range (d99877,
* d44356) or exceeds the maximum number of possible
* volumes given the current SVM configuration.
*
* - all available and unavailable device specifications refer
* to existing controllers, disks, or slices on the system.
* Examples of bad data:
*
* - a valid but non-existent controller (c23, c2)
* - a valid but non-existent disk (c0t0d8, c1t0d0)
* - a valid slice on a non-existent disk or controller
* (c0t0d8s7, c1t0d05)
* - a valid slice on an existing disk (c0t0d0s12,
* c0t0d0s9)
*
* - any typed volume request that explicitly specifies components
* requires additional validation to detect syntactically valid
* expressions that are semantically ambiguous:
*
* a concat request that:
* - specifies size and components is invalid
*
* a stripe request that:
* - specifies size and components is invalid
* - specifies mincomp and components but not enough
* components is invalid
* - specifies maxcomp and components but too many
* components is invalid
*
* a HSP request that:
* - specifies components that are not appropriate for
* the volumes the HSP serves is invalid (?)
*
* a stripe, concat or HSP request that:
* - specifies a component that was used in a prior
* request is invalid
* - specifies a component that does not exist in the
* diskset is invalid (e.g., c0t0d0s0, but c0t0d0 is
* not yet in the diskset)
*
* a mirror request that:
* - specifies nsubs and components but not enough
* components is invalid
* - specifies components and the components specify
* different sizes results in a WARNING since the total
* usable capacity of the mirror is determined by the
* smallest of its submirrors.
* - specifies components and the components specify
* components results in a WARNING since the submirrors
* may end up with different sizes
*/
static int validate_request_name(
devconfig_t *req,
component_type_t type);
static int validate_request_size(
devconfig_t *req,
component_type_t type);
static int validate_minimum_size(
uint64_t nbytes);
static uint64_t apply_layout_overhead_factor(
uint64_t req_size);
static int get_space_available_for_request(
devconfig_t *request,
dlist_t *usable_slices,
uint64_t *avail_space);
static int do_available_space_check(
uint64_t req_size,
uint64_t raw_avail_space,
devconfig_t *request,
dlist_t *usable_slices);
static int validate_request_redundancy_level(
devconfig_t *req);
static int validate_request_npaths(
devconfig_t *req);
static int validate_request_submirrors(
devconfig_t *req);
static int validate_submirror_types(
dlist_t *submirrors);
static int validate_submirror_number(
devconfig_t *req,
dlist_t *submirrors);
static int validate_submirror_sizes(
devconfig_t *req,
dlist_t *submirrors);
static int validate_submirror_size_and_components(
devconfig_t *submir,
uint64_t mirror_size,
uint64_t *assumed_size,
dlist_t **submirs_with_size,
dlist_t **submirs_with_comps,
dlist_t **submirs_no_size_or_comps);
static int validate_slice_components(
devconfig_t *req,
component_type_t type);
static char *get_device_aliases_string(
dm_descriptor_t desc);
static int validate_device_array(
char **array,
char *which,
dlist_t **list);
static int add_reserved_name(char *name);
static boolean_t is_rsvd_name(char *name);
static dlist_t *_rsvd_names = NULL;
/*
* FUNCTION: release_validatation_caches()
*
* RETURNS: int - 0
*
* PURPOSE: Cleanup function.
*
* Purges list of reserved volume names. Should be called
* after all layout requests have been processed.
*/
int
release_validation_caches()
{
dlist_free_items(_rsvd_names, NULL);
_rsvd_names = NULL;
return (0);
}
/*
* FUNCTION: validate_basic_svm_config()
*
* RETURNS: int - 0 on success
* !0 on failure
*
* PURPOSE: Check to see if the local set metadb replicas have been created.
*
* Makes sure at least 1 metadb replica exists for the local set.
*/
int
validate_basic_svm_config()
{
int error = 0;
int nreplicas = 0;
if ((error = get_n_metadb_replicas(&nreplicas)) == 0) {
if (nreplicas == 0) {
volume_set_error(
gettext("Failed: State database replicas must "
"exist before using %s.\n"
"See metadb(1M) and %s(1M)."),
progname, progname);
error = -1;
} else {
oprintf(OUTPUT_DEBUG,
gettext("%d metadb replicas found.\n"),
nreplicas);
}
}
return (error);
}
/*
* FUNCTION: validate_request_sizes(devconfig_t *req)
*
* INPUT: req: a devconfig_t pointer to the toplevel request
*
* RETURNS: int - 0 on success
* !0 on failure
*
* PURPOSE: Check to see if the any of the individual volume request
* sizes exceeds the raw available space on the system or
* the space available to that specific request.
*
* Check to see if the total space for all requests exceeds
* the raw available space.
*
* If any check fails, stop checking, emit an error and
* return -1.
*
* Note: this function must be called after the slice
* usages have been determined and the list of usable
* slices has been generated.
*/
int
validate_request_sizes(
devconfig_t *request)
{
int error = 0;
dlist_t *usable_slices;
dlist_t *iter;
char bad_rqst_info[BUFSIZ];
uint64_t bad_rqst_space = 0;
uint64_t total_rqst_space = 0;
uint64_t raw_space = 0;
(void) get_usable_slices(&usable_slices);
/*
* calculate raw available space: space on slices that are
* "available" based on the diskset defaults or global defaults
*/
if ((error = get_space_available_for_request(request,
usable_slices, &raw_space)) != 0) {
return (error);
}
if (raw_space == 0) {
volume_set_error(
gettext("Failed: there is no available space.\n"));
return (-1);
}
/* deduct sizes of reserved components */
(void) get_reserved_slices(&iter);
for (; (iter != NULL) && (raw_space != 0) && (error == 0);
iter = iter->next) {
dm_descriptor_t slice = (uintptr_t)iter->obj;
uint64_t nbytes;
if ((error = slice_get_size(slice, &nbytes)) == 0) {
if (raw_space >= nbytes) {
raw_space -= nbytes;
} else {
raw_space = 0;
}
}
}
/*
* check each volume request's size against raw_space,
* if that looks ok, do a closer check with the request's
* available devices
*/
iter = devconfig_get_components(request);
for (; (iter != NULL) && (error == 0); iter = iter->next) {
devconfig_t *req = (devconfig_t *)iter->obj;
component_type_t type = TYPE_UNKNOWN;
char *typestr = NULL;
uint64_t nbytes = 0;
(void) devconfig_get_type(req, &type);
if (type == TYPE_HSP) {
continue;
}
typestr = devconfig_type_to_str(type);
if ((error = devconfig_get_size(req, &nbytes)) == 0) {
/* check specified size */
if (type == TYPE_CONCAT || type == TYPE_STRIPE) {
if ((error = do_available_space_check(
apply_layout_overhead_factor(nbytes),
raw_space, req, usable_slices)) == 0) {
total_rqst_space += nbytes;
} else if (error == ENOSPC || error == E2BIG) {
(void) snprintf(bad_rqst_info, BUFSIZ-1,
"%s", typestr);
bad_rqst_space = nbytes;
}
} else if (type == TYPE_MIRROR) {
uint16_t nsubs = 0;
if ((error = get_mirror_nsubs(req, &nsubs)) == 0) {
if ((error = do_available_space_check(
apply_layout_overhead_factor(nbytes * nsubs),
raw_space, req, usable_slices)) == 0) {
total_rqst_space += (nsubs * nbytes);
} else {
(void) snprintf(bad_rqst_info, BUFSIZ-1,
gettext("%s with %d submirrors"),
typestr, nsubs);
bad_rqst_space = nbytes;
}
}
}
} else if ((error == ERR_ATTR_UNSET) && (type == TYPE_MIRROR)) {
/* mirror specified no size: find submirror that does */
dlist_t *subs = devconfig_get_components(req);
error = 0;
if (subs != NULL) {
dlist_t *iter2;
int nsubs = dlist_length(subs);
for (iter2 = subs;
(iter2 != NULL) && (error == 0);
iter2 = iter2->next) {
devconfig_t *sub = (devconfig_t *)iter2->obj;
if ((error = devconfig_get_size(sub, &nbytes)) == 0) {
if ((error = do_available_space_check(
apply_layout_overhead_factor(nbytes * nsubs),
raw_space, req, usable_slices)) == 0) {
total_rqst_space += (nbytes * nsubs);
} else {
(void) snprintf(bad_rqst_info, BUFSIZ-1,
gettext("%s with %d submirrors"),
typestr, nsubs);
bad_rqst_space = nbytes;
}
break;
} else if (error == ERR_ATTR_UNSET) {
error = 0;
}
}
}
}
}
/*
* do_available_space_check may return ENOSPC or E2BIG
*/
if (error == ENOSPC) {
char *sizestr = NULL;
(void) bytes_to_sizestr(bad_rqst_space,
&sizestr, universal_units, B_FALSE);
volume_set_error(
gettext("Failed: the request for a %s %s "
"exceeds the available space.\n"),
sizestr, bad_rqst_info);
free(sizestr);
error = -1;
} else if (error == E2BIG) {
char *sizestr = NULL;
(void) bytes_to_sizestr(bad_rqst_space,
&sizestr, universal_units, B_FALSE);
volume_set_error(
gettext("Failed: the request for a %s %s "
"exceeds the usable space on the device(s) "
"specified as available.\n"),
sizestr, bad_rqst_info);
free(sizestr);
error = -1;
} else if (apply_layout_overhead_factor(total_rqst_space) > raw_space) {
char *sizestr = NULL;
(void) bytes_to_sizestr(
total_rqst_space, &sizestr, universal_units, B_FALSE);
volume_set_error(
gettext("Failed: the total space requested for the "
"volumes (about %s) exceeds the available "
"space.\n"),
sizestr);
free(sizestr);
error = -1;
}
return (error);
}
/*
* FUNCTION: apply_layout_overhead_factor(uint64_t req_size)
*
* INPUT: req_size: a requested volume size
*
* RETURNS: the requested volume size with an overhead factor applied
*
* PURPOSE: The input size size is inflated by a "fudge" factor
* to account for some of the expected overhead required for
* volumes such as block and cylinder boundary alignment.
*/
static uint64_t
apply_layout_overhead_factor(
uint64_t req_size)
{
double overhead = 1.15;
double d_size = req_size;
uint64_t result = (uint64_t)(d_size * overhead);
return (result);
}
/*
* FUNCTION: get_space_available_for_request(devconfig_t *request,
* dlist_t *usable_slices, uint64_t *avail_space)
*
* INPUT: request: a devconfig_t volume request
* usable_slices: a list of usable slice dm_descriptor_t handles
*
* OUTPUT: avail_space: the total space on slices in the usable_slice
* list that is available for use by the input
* request.
*
* RETURNS: int - 0 on success
* !0 on failure
*
* PURPOSE: Iterate the input list of usable slices, determine which are
* available to the input request and accumulate the total space
* they represent.
*
* The slices in the usable_slice list are those with no apparent
* usage detected. The slice_is_available() check determines
* whether the slice passes the available/unavailable device
* specification associated with the input request.
*/
static int
get_space_available_for_request(
devconfig_t *request,
dlist_t *usable_slices,
uint64_t *avail_space)
{
dlist_t *iter;
int error = 0;
*avail_space = 0;
for (iter = usable_slices;
(iter != NULL) && (error == 0);
iter = iter->next) {
dm_descriptor_t slice = (uintptr_t)iter->obj;
char *sname;
uint64_t nbytes;
boolean_t avail = B_FALSE;
if ((error = get_display_name(slice, &sname)) == 0) {
if ((error = slice_is_available(sname, request, &avail)) == 0) {
if (avail == B_TRUE) {
if ((error = slice_get_size(slice, &nbytes)) == 0) {
*avail_space += nbytes;
}
}
}
}
}
return (error);
}
/*
* FUNCTION: do_available_space_check(uint64_t req_size,
* uint64_t raw_avail_space, devconfig_t *request,
* dlist_t *usable_slices)
*
* INPUT: req_size: the requested size of a volume
* raw_avail_space:the total available space for all volumes
* request: a devconfig_t volume request
* usable_slices: a list of usable slice dm_descriptor_t handles
*
* RETURNS: int - ENOSPC if the requested size exceeds the raw
* available space.
*
* E2BIG if the requested size exceeds the space
* available specifically to the input request,
* taking into account its available and
* unavailable device specifications.
*
* 0 otherwise
*
* PURPOSE: Check the input request size against different forms of
* available space.
*
* If the requested size is less than the raw_avail_space, do the
* more expensive check against the space specifically available
* to the input request.
*/
static int
do_available_space_check(
uint64_t req_size,
uint64_t raw_avail_space,
devconfig_t *request,
dlist_t *usable_slices)
{
int error = 0;
if (req_size > raw_avail_space) {
error = ENOSPC;
} else {
uint64_t avail_space = 0;
if ((error = get_space_available_for_request(request,
usable_slices, &avail_space)) == 0) {
if (req_size > avail_space) {
error = E2BIG;
}
}
}
return (error);
}
/*
* FUNCTION: validate_request(devconfig_t *req)
*
* INPUT: req - a devconfig_t representing a volume layout request.
*
* RETURNS: int - 0 if the request passes validation
* !0 otherwise.
*
* PURPOSE: Main entry point into the layout request semantic
* validatation.
*
* Determines the type of volume requested and invokes the
* appropriate validation functions.
*/
int
validate_request(
devconfig_t *req)
{
int error = 0;
component_type_t type = TYPE_UNKNOWN;
((error = validate_request_avail_unavail(req)) != 0) ||
(error = devconfig_get_type(req, &type));
if (error != 0) {
return (error);
}
if (type == TYPE_MIRROR) {
((error = validate_request_name(req, type)) != 0) ||
(error = validate_request_size(req, type)) ||
(error = validate_request_submirrors(req));
} else if (type == TYPE_CONCAT || type == TYPE_STRIPE) {
((error = validate_request_name(req, type)) != 0) ||
(error = validate_request_size(req, type)) ||
(error = validate_slice_components(req, type));
} else if (type == TYPE_HSP) {
((error = validate_request_name(req, type)) != 0) ||
(error = validate_slice_components(req, type));
} else if (type == TYPE_VOLUME) {
((error = validate_request_name(req, type)) != 0) ||
(error = validate_request_redundancy_level(req)) ||
(error = validate_request_npaths(req));
}
return (error);
}
/*
* FUNCTION: validate_reserved_slices()
*
* RETURNS: int - 0 if all reserved slices are usable in
* new devices.
* !0 otherwise.
*
* PURPOSE: Ensures that each reserved slice is actually usable
* as a volume component.
*
* Retrieves list of reserved slices and list of usable
* slices. Ensures that each reserved slice is in the
* usable list, generates an error if it is not.
*
* This is broken out as a separate function because
* initial validation is using the lists of all known
* devices. Device "usability" is only determined after
* the initial validation has completed successfully.
*/
int
validate_reserved_slices()
{
dlist_t *reserved_slices;
dlist_t *usable_slices;
int error = 0;
((error = get_reserved_slices(&reserved_slices)) != 0) ||
(error = get_usable_slices(&usable_slices));
if (error == 0) {
dlist_t *iter;
for (iter = reserved_slices;
(iter != NULL) && (error == 0);
iter = iter->next) {
if (dlist_contains(usable_slices, iter->obj,
compare_descriptor_names) != B_TRUE) {
dm_descriptor_t slice = (uintptr_t)iter->obj;
char *name = NULL;
error = get_display_name(slice, &name);
if (error == 0) {
char *aliases = get_device_aliases_string(slice);
if (aliases[0] != NULL) {
volume_set_error(
gettext("A requested volume component "
"is currently in use: \"%s\" "
"(aliases: %s).\n"),
name, aliases);
} else {
volume_set_error(
gettext("A requested volume component "
"is currently in use: \"%s\"\n"),
name);
}
error = -1;
}
}
}
}
return (error);
}
/*
* FUNCTION: validate_request_avail_unavail(devconfig_t *req)
*
* INPUT: req - a devconfig_t representing a volume layout request.
*
* RETURNS: int - 0 if the request passes validation
* !0 otherwise.
*
* PURPOSE: validation function for a request's lists of available
* and unavailable devices.
*
* validates that both lists contain names of known devices.
*
* validates that the same name does not appear in both lists.
*/
int
validate_request_avail_unavail(
devconfig_t *req)
{
dlist_t *avail = NULL;
dlist_t *unavail = NULL;
int error = 0;
/* check that each array contains valid devices */
((error = validate_device_array(devconfig_get_available(req),
gettext("available"), &avail)) != 0) ||
(error = validate_device_array(devconfig_get_unavailable(req),
gettext("unavailable"), &unavail));
/* check that the arrays don't both contain the same device(s) */
if (error == 0) {
dlist_t *iter;
for (iter = avail; iter != NULL; iter = iter->next) {
if (dlist_contains(unavail, iter->obj,
compare_descriptor_names) == B_TRUE) {
char *name;
char *aliases =
get_device_aliases_string((uintptr_t)iter->obj);
(void) get_display_name((uintptr_t)iter->obj, &name);
if (aliases[0] != NULL) {
volume_set_error(
gettext("\"%s\" specified as both available "
"and unavailable.\n"
"It has these aliases: %s\n"),
name, aliases);
} else {
volume_set_error(
gettext("\"%s\" specified as both available "
"and unavailable.\n"),
name);
}
error = -1;
break;
}
}
}
dlist_free_items(avail, NULL);
dlist_free_items(unavail, NULL);
return (error);
}
/*
* FUNCTION: validate_device_array(char **array, char *which, dlist_t **list)
*
* INPUT: array - an array of char * device names
* which - either "available" or "unavailable"
* indicating the array name to use in
* error strings.
* OUTPUT: list - a list of device descriptors corresponding the each
* of the input names.
*
* RETURNS: int - 0 if the array passes validation
* !0 otherwise.
*
* PURPOSE: validation function for a request's list of available
* or unavailable devices.
*
* DID names are converted to CTD names.
*
* The CTD name must be of an available slice, disk or
* HBA, or a known used slice, disk or HBA that was
* discovered when the system's devices were probed.
*
* Any other name is assumed to refer to a device not
* attached to the system and results in a validation
* failure.
*
* Descriptors for validated devices are added to the input
* list.
*/
int
validate_device_array(
char **array,
char *which,
dlist_t **list)
{
int error = 0;
int i = 0;
if (array == NULL || *array == NULL) {
return (0);
}
for (i = 0; (array[i] != NULL) && (error == 0); i++) {
dm_descriptor_t slice = (dm_descriptor_t)0;
dm_descriptor_t disk = (dm_descriptor_t)0;
dm_descriptor_t hba = (dm_descriptor_t)0;
char *name = array[i];
/* name must correspond to a known HBA, disk, or slice */
if ((error = hba_get_by_name(name, &hba)) == 0) {
if (hba == (dm_descriptor_t)0) {
if ((error = disk_get_by_name(name, &disk)) == 0) {
if (disk == (dm_descriptor_t)0) {
error = slice_get_by_name(name, &slice);
}
}
}
}
if (error != 0) {
break;
}
/* 0 sized slices cannot be used as-is, pretend non-existant */
if (slice != (dm_descriptor_t)0) {
uint64_t size = 0;
if ((error = slice_get_size(slice, &size)) == 0) {
if (size == 0) {
slice = (dm_descriptor_t)0;
}
}
}
oprintf(OUTPUT_DEBUG,
gettext(" validate %s (%s): s=%llu, d=%llu, c=%llu\n"),
which, array[i], slice, disk, hba);
if ((error == 0) && ((slice != 0) || (disk != 0) || (hba != 0))) {
/* name represents an individual "device", add it to the list */
dm_descriptor_t desc = (dm_descriptor_t)0;
dlist_t *item;
if (slice != 0) {
desc = slice;
} else if (disk != 0) {
desc = disk;
} else if (hba != 0) {
desc = hba;
}
if ((item = dlist_new_item((void *)(uintptr_t)desc)) == NULL) {
error = ENOMEM;
} else {
*list = dlist_append(item, *list, AT_HEAD);
}
} else if (is_ctd_target_name(name) == B_TRUE) {
/* expand target to all of its disks */
dlist_t *disks = NULL;
if ((error = get_disks_for_target(name, &disks)) == 0) {
if ((disks == NULL) || (dlist_length(disks) == 0)) {
volume_set_error(
gettext("nonexistent device specified "
"as %s: \"%s\"."),
which, array[i]);
error = -1;
} else {
dlist_t *iter;
for (iter = disks;
(iter != NULL) && (error == 0);
iter = iter->next) {
dlist_t *item;
if ((item = dlist_new_item(iter->obj)) == NULL) {
error = ENOMEM;
} else {
*list = dlist_append(item, *list, AT_HEAD);
}
}
}
}
} else {
/* not a slice, disk, target or ctrl */
volume_set_error(
gettext("nonexistent device specified "
"as %s: \"%s\"."),
which, array[i]);
error = -1;
}
}
return (error);
}
/*
* FUNCTION: validate_request_name(devconfig_t *req, component_type_t type)
*
* INPUT: req - a devconfig_t volume request
* type - the volume type being requested
*
* SIDEEFFECT: if the request specifies a name and the name is valid and
* not currently in use an attempt is made to reserve it.
* if the name has already been reserved by a prior volume
* request, validation fails.
*
* RETURNS: int - 0 if the requested name passes validation
* (or there is no name request)
* !0 otherwise.
*
* PURPOSE: Validation function for a request's volume name.
*
* a HSP name must be valid and reservable.
*
* a volume name must be valid and reservable.
*/
static int
validate_request_name(
devconfig_t *req,
component_type_t type)
{
char *name = NULL;
char *typestr = devconfig_type_to_str(type);
int error = 0;
if ((error = devconfig_get_name(req, &name)) != 0) {
if (error != ERR_ATTR_UNSET) {
volume_set_error(
gettext("error getting requested name.\n"));
return (error);
}
/* no name specified */
return (0);
}
if (type == TYPE_HSP) {
if (is_hsp_name_valid(name) == 0) {
volume_set_error(
gettext("requested %s name \"%s\" is not valid.\n"),
typestr, name);
error = -1;
} else if (reserve_hsp_name(name) != 0) {
if (is_rsvd_name(name) == B_TRUE) {
volume_set_error(
gettext("requested %s name \"%s\" used "
"previously in this request.\n"),
typestr, name);
} else {
volume_set_error(
gettext("requested %s name \"%s\" is not "
"available.\n"),
typestr, name);
}
error = -1;
} else {
error = add_reserved_name(name);
}
} else {
if (is_volume_name_valid(name) == 0) {
volume_set_error(
gettext("requested %s name \"%s\" is not valid.\n"),
typestr, name);
error = -1;
} else if (is_volume_name_in_range(name) != B_TRUE) {
int max = 0;
(void) get_max_number_of_devices(&max);
volume_set_error(
gettext("requested %s name \"%s\" is not legal.\n"
"Use a name less than d%d.\n"),
typestr, name, max);
error = -1;
} else if (reserve_volume_name(name) != 0) {
if (is_rsvd_name(name) == B_TRUE) {
volume_set_error(
gettext("requested %s name \"%s\" used "
"previously in this request.\n"),
typestr, name);
} else {
volume_set_error(
gettext("requested %s name \"%s\" is not "
"available, a volume with that name "
"already exists.\n"),
typestr, name);
}
error = -1;
} else {
error = add_reserved_name(name);
}
}
return (error);
}
/*
* FUNCTION: add_reserved_name(char *name)
*
* INPUT: name - a char * volume name
*
* RETURNS: int - 0 on success
* !0 otherwise.
*
* PURPOSE: Helper which remembers specfically requested names
* in a private list to ensure that the same name isn't
* requested more than once.
*/
static int
add_reserved_name(
char *name)
{
dlist_t *item = NULL;
if ((item = dlist_new_item(name)) == NULL) {
return (ENOMEM);
}
_rsvd_names = dlist_append(item, _rsvd_names, AT_TAIL);
return (0);
}
/*
* FUNCTION: is_rsvd_name(char *name)
*
* INPUT: name - a char * volume name
*
* RETURNS: boolean_t - B_TRUE if the requested name is currently
* reserved, B_FALSE otherwise.
*
* PURPOSE: Helper which checks to see if the input volume
* name was previously reserved.
*/
static boolean_t
is_rsvd_name(
char *name)
{
dlist_t *iter = NULL;
for (iter = _rsvd_names; iter != NULL; iter = iter->next) {
if ((string_case_compare(name, (char *)iter->obj)) == 0) {
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* FUNCTION: validate_request_size(devconfig_t *req, component_type_t type)
*
* INPUT: req - a devconfig_t volume request
* type - the volume type being requested
*
* RETURNS: int - 0 if the requested size passes validation
* (or there is no size request)
* !0 otherwise.
*
* PURPOSE: Validation function for a request's volume size.
*
* a HSP request can have no size.
*
* a concat, stripe or mirror request may have a size.
* if size is specified, the request cannot also specify
* components. Conversely, if the request does not specify
* a size, it must specify components.
*/
static int
validate_request_size(
devconfig_t *req,
component_type_t type)
{
uint64_t nbytes = 0;
int error = 0;
if (type == TYPE_HSP) {
return (0);
}
if ((error = devconfig_get_size(req, &nbytes)) != 0) {
if (error == ERR_ATTR_UNSET) {
/* nbytes not specified, request must have subcomponents */
dlist_t *list = devconfig_get_components(req);
if (list != NULL && dlist_length(list) > 0) {
error = 0;
} else {
volume_set_error(
gettext("%s request specifies no size or "
"subcomponents.\n"),
devconfig_type_to_str(type));
error = -1;
}
}
return (error);
}
return (error);
}
/*
* FUNCTION: validate_minimum_size(uint64_t nbytes)
*
* INPUT: nbytes - requested volume size in bytes
*
* RETURNS: int - 0 if the requested size passes validation
* (or there is no size request)
* !0 otherwise.
*
* PURPOSE: Validation function for a request's volume size.
*
* an error is issued if the requested size <= 512K.
*/
static int
validate_minimum_size(
uint64_t nbytes)
{
static uint64_t min = (512 * 1024) - 1;
int error = 0;
if (nbytes <= min) {
char *sizestr = NULL;
char *minstr = NULL;
(void) bytes_to_sizestr(
nbytes, &sizestr, universal_units, B_FALSE);
(void) bytes_to_sizestr(
min, &minstr, universal_units, B_FALSE);
volume_set_error(
gettext("requested volume size (%s) must be "
"greater than %s.\n"),
sizestr, minstr);
free(sizestr);
free(minstr);
error = -1;
}
return (error);
}
/*
* FUNCTION: validate_request_redundancy_level(devconfig_t *req)
*
* INPUT: req - a devconfig_t volume request
*
* RETURNS: int - 0 if the requested redundancy level
* passes validation (or none was requested)
* !0 otherwise.
*
* PURPOSE: Validation function for a redundant volume request's
* redundancy level.
*
* If the request specifies redundancy, the value must be
* between 1 and 4.
*/
static int
validate_request_redundancy_level(
devconfig_t *req)
{
uint16_t rlevel = 0;
int error = 0;
if ((error = devconfig_get_volume_redundancy_level(
req, &rlevel)) != 0) {
if (error == ERR_ATTR_UNSET) {
error = 0;
}
return (error);
}
if (rlevel > 4) {
volume_set_error(gettext(
"requested redundancy level must be between 0 and 4.\n"));
error = -1;
}
return (error);
}
/*
* FUNCTION: validate_request_npaths(devconfig_t *req)
*
* INPUT: req - a devconfig_t volume request
*
* RETURNS: int - 0 if the requested # of redundant data paths
* passes validation (or none was requested)
* !0 otherwise.
*
* PURPOSE: Validation function for a volume request's number of
* redundant data paths. This value controls the number
* of independent data paths slices components selected
* for the volume should have.
*
* If the request specifies npaths, the value must be
* between 1 and 4 (4 is an arbitrary upper limit, there
* is no known physical limit).
*/
static int
validate_request_npaths(
devconfig_t *req)
{
uint16_t npaths = 0;
uint16_t minpaths = 1;
uint16_t maxpaths = 4;
int error = 0;
if ((error = devconfig_get_volume_npaths(req, &npaths)) != 0) {
if (error == ERR_ATTR_UNSET) {
error = 0;
}
return (error);
}
if (npaths < minpaths || npaths > maxpaths) {
volume_set_error(
gettext("requested number of redundant paths must be "
"between %d and %d.\n"), minpaths, maxpaths);
error = -1;
}
if ((npaths > 1) && (is_mpxio_enabled() != B_TRUE)) {
volume_set_error(
gettext("requested number of redundant paths (%d) cannot "
"be provided, MPXIO is not enabled on this "
"system."),
npaths);
error = -1;
}
return (error);
}
/*
* FUNCTION: validate_request_submirrors(devconfig_t *req)
*
* INPUT: req - a devconfig_t volume request
*
* RETURNS: int - 0 if the requested mirror's submirrors
* pass validation
* !0 otherwise.
*
* PURPOSE: Validation function for a mirror volume request's
* explicitly specified submirror components.
*
* Items to check:
* a. submirror types
* b. submirror number
* c. submirror sizes
*/
static int
validate_request_submirrors(
devconfig_t *req)
{
dlist_t *submirrors = NULL;
int error = 0;
submirrors = devconfig_get_components(req);
((error = validate_submirror_types(submirrors)) != 0) ||
(error = validate_submirror_number(req, submirrors)) ||
(error = validate_submirror_sizes(req, submirrors));
return (error);
}
/*
* FUNCTION: validate_submirror_types(dlist_t *subs)
*
* INPUT: subs - a list of submirror requests
*
* RETURNS: int - 0 if the requested submirrors
* pass validation
* !0 otherwise.
*
* PURPOSE: Validation function for a mirror volume request's
* explicitly specified submirror components.
*
* Checks that each requested submirror request
* is for a concat or stripe.
*/
static int
validate_submirror_types(
dlist_t *submirrors)
{
dlist_t *iter;
int error = 0;
/* specified submirrors must be stripes or concats */
for (iter = submirrors;
(iter != NULL) && (error == 0);
iter = iter->next) {
devconfig_t *submir = (devconfig_t *)iter->obj;
component_type_t submirtype = TYPE_UNKNOWN;
if ((error = devconfig_get_type(submir, &submirtype)) != 0) {
volume_set_error(
gettext("failed to get requested component type.\n"));
break;
}
if (submirtype != TYPE_CONCAT && submirtype != TYPE_STRIPE) {
volume_set_error(
gettext("requested submirror type \"%s\" "
"is not valid.\n"),
devconfig_type_to_str(submirtype));
error = -1;
break;
}
}
return (error);
}
/*
* FUNCTION: validate_submirror_number(devconfig_t *req, dlist_t *subs)
*
* INPUT: req - the mirror request
* subs - the list of requested submirrors
*
* RETURNS: int - 0 if the requested submirrors
* pass validation
* !0 otherwise.
*
* PURPOSE: Validation function for a mirror volume request's
* explicitly specified submirror components.
*
* Checks that the number of submirror components
* that have been specified matches the number of
* submirrors specified.
*/
static int
validate_submirror_number(
devconfig_t *req,
dlist_t *submirrors)
{
uint16_t nsubs = 0;
int error = 0;
if ((error = devconfig_get_mirror_nsubs(req, &nsubs)) != 0) {
if (error == ERR_ATTR_UNSET) {
/* not specified */
error = 0;
}
} else if ((submirrors != NULL) &&
(dlist_length(submirrors) != nsubs)) {
volume_set_error(
gettext("the requested number of submirrors (%d) differs "
"from the number of specified submirrors (%d).\n"),
nsubs, dlist_length(submirrors));
error = -1;
}
return (error);
}
/*
* FUNCTION: validate_submirror_sizes(devconfig_t *req,
* dlist_t *submirrors)
*
* INPUT: req - the mirror request
* submirrors - the list of requested submirrors
*
* RETURNS: int - 0 if the requested submirrors
* pass validation
* !0 otherwise.
*
* PURPOSE: Validation function for a mirror volume request's
* explicitly specified size. Assumes that the mirror's size
* has been validated by validate_request_size().
*
* Compares explicitly requested mirror size against specified
* component sizes and checks:
*
* - any submirror request that specifies both size and
* components is invalid
* - any submirror request specifying a size different
* than that explictly requested for the mirror is
* invalid
* - a submirror request specifying a size < 512K is invalid.
*
* Other validation/warnings:
*
* - submirrors that specify components may end up with
* usable capacity that differs from what was specified
* for the mirror.
*
* - submirrors which specify neither size nor components are
* assumed to be the size requested for the mirror. If the
* mirror size is not specified, the first explicit size for
* a submirror is assumed as the size for the mirror.
*/
static int
validate_submirror_sizes(
devconfig_t *req,
dlist_t *submirrors)
{
dlist_t *submirs_with_size = NULL;
dlist_t *submirs_with_comps = NULL;
dlist_t *submirs_with_nothing = NULL;
dlist_t *iter = NULL;
uint64_t mirror_size = 0;
uint64_t assumed_size = 0;
int error = 0;
if (submirrors == NULL || dlist_length(submirrors) == 0) {
return (0);
}
if ((error = devconfig_get_size(req, &mirror_size)) != 0) {
if (error == ERR_ATTR_UNSET) {
error = 0;
} else {
return (error);
}
}
/*
* check size and component for each submirror,
* collect those that specify size, components or neither
* into separate lists.
*/
for (iter = submirrors;
(iter != NULL) && (error == 0);
iter = iter->next) {
devconfig_t *submir = (devconfig_t *)iter->obj;
error = validate_submirror_size_and_components(submir,
mirror_size, &assumed_size, &submirs_with_size,
&submirs_with_comps, &submirs_with_nothing);
}
if (error == 0) {
int n_size = dlist_length(submirs_with_size);
int n_comp = dlist_length(submirs_with_comps);
int n_none = dlist_length(submirs_with_nothing);
if ((n_size != 0) && (n_comp != 0)) {
/* some submirrors specified size, some components */
oprintf(OUTPUT_TERSE,
gettext(" *** warning: %d submirrors are specified "
"by size, %d specified by components.\n"
" The resulting mirror capacity will be "
"that of the smallest submirror.\n"),
n_size, n_comp);
}
if (n_none != 0) {
if (assumed_size != 0) {
/* some submirrors specified neither size or components */
char *sizestr = NULL;
(void) bytes_to_sizestr(
assumed_size, &sizestr, universal_units, B_FALSE);
oprintf(OUTPUT_TERSE,
gettext(" *** warning: %d submirrors specified "
"neither size or components,\n"
" the assumed size is %s.\n"),
n_none, sizestr);
free(sizestr);
} else if (mirror_size == 0) {
volume_set_error(
gettext("no size specified for requested "
"mirror and no sizes/components "
"specified for its submirrors."));
error = -1;
}
}
dlist_free_items(submirs_with_size, NULL);
dlist_free_items(submirs_with_comps, NULL);
dlist_free_items(submirs_with_nothing, NULL);
}
return (error);
}
/*
* FUNCTION: validate_submirror_size_and_components(
* devconfig_t *submir,
* uint64_t mirror_size,
* uint64_t *assumed_size,
* dlist_t **submirs_with_size,
* dlist_t **submirs_with_comps,
* dlist_t **submirs_no_size_or_comps)
*
* INPUT: submir - a specific submirror request
* mirror_size, - the size specified for the mirror
*
* OUTPUT: assumed_size - the assumed size of the mirror,
* if none specified.
* submirs_with_size - pointer to a list of submirror
* requests that specify a size
* submirs_with_comps - pointer to a list of submirror
* requests that specify components
* submirs_no_size_or_comps - pointer to a list of
* submirror requests that specify neither
* a size or components
*
* RETURNS: int - 0 if the requested submirrors
* pass validation
* !0 otherwise.
*
* PURPOSE: Validation function which checks a specific submirror
* request's size and components against the parent mirror's
* size.
*
* - any submirror request that specifies both size and
* components is invalid
* - any submirror request specifying a size different
* than that explictly requested for the mirror is
* invalid
* - a submirror request specifying a size < 512K is invalid.
* - any components specified for a submirror are validated.
*
* If the submirror passes the validation checks, it is added
* to the appropriate output list.
*
* If the input mirror_size is 0 and the submirror specifies
* a valid size, the submirror size is returned as the
* assumed_size for the mirror.
*/
static int
validate_submirror_size_and_components(
devconfig_t *submir,
uint64_t mirror_size,
uint64_t *assumed_size,
dlist_t **submirs_with_size,
dlist_t **submirs_with_comps,
dlist_t **submirs_no_size_or_comps)
{
uint64_t submir_size = 0;
component_type_t submir_type = TYPE_UNKNOWN;
char *submir_typestr = NULL;
dlist_t *submir_comps = NULL;
dlist_t *item = NULL;
int n_submir_comps = 0;
int error = 0;
submir_comps = devconfig_get_components(submir);
if (submir_comps != NULL) {
n_submir_comps = dlist_length(submir_comps);
}
if ((error = devconfig_get_size(submir, &submir_size)) != 0) {
if (error == ERR_ATTR_UNSET) {
/* submirror size not specified */
error = 0;
submir_size = 0;
}
}
if (error != 0) {
return (error);
}
/* submirror type previously validated */
(void) devconfig_get_type(submir, &submir_type);
submir_typestr = devconfig_type_to_str(submir_type);
if (submir_size == 0) {
/* submirror has no size, components? */
if (n_submir_comps > 0) {
/* validate components */
error = validate_slice_components(submir, submir_type);
item = dlist_new_item((void *)submir);
if (item == NULL) {
error = ENOMEM;
} else {
*submirs_with_comps =
dlist_append(item, *submirs_with_comps, AT_TAIL);
}
} else {
/* no size or components */
item = dlist_new_item((void *)submir);
if (item == NULL) {
error = ENOMEM;
} else {
*submirs_no_size_or_comps =
dlist_append(item, *submirs_no_size_or_comps, AT_TAIL);
}
}
} else {
/* submirror has size, check it */
if (error == 0) {
error = validate_minimum_size(submir_size);
}
/* check size against mirror's size */
if ((error == 0) && (submir_size != mirror_size)) {
if (mirror_size != 0) {
/* sizes differ */
char *sizestr = NULL;
char *mstr = NULL;
(void) bytes_to_sizestr(
submir_size, &sizestr, universal_units, B_FALSE);
(void) bytes_to_sizestr(
mirror_size, &mstr, universal_units, B_FALSE);
volume_set_error(
gettext("the requested submirror size (%s) "
"differs from the requested mirror "
"size (%s).\n"),
sizestr, mstr);
error = -1;
free(sizestr);
free(mstr);
} else if (*assumed_size == 0) {
/* first size assumed as mirror size */
char *sizestr = NULL;
(void) bytes_to_sizestr(
submir_size, &sizestr, universal_units, B_FALSE);
oprintf(OUTPUT_TERSE,
gettext(" *** warning, using first "
"explicit submirror size (%s)\n"
" as the mirror size\n"),
sizestr);
*assumed_size = submir_size;
free(sizestr);
} else if (submir_size != *assumed_size) {
/* submirror sizes differ */
char *sizestr1 = NULL;
char *sizestr2 = NULL;
(void) bytes_to_sizestr(
submir_size, &sizestr1, universal_units, B_FALSE);
(void) bytes_to_sizestr(
*assumed_size, &sizestr2, universal_units, B_FALSE);
volume_set_error(
gettext("submirror specifies different "
"size (%s) than a previous "
"submirror (%s)\n"),
sizestr1, sizestr2);
free(sizestr1);
free(sizestr2);
error = -1;
}
}
if ((error == 0) && (n_submir_comps > 0)) {
/* size and subcomponents specified */
char *sizestr = NULL;
(void) bytes_to_sizestr(
submir_size, &sizestr, universal_units, B_FALSE);
volume_set_error(
gettext("%s submirror specifies both an "
"explicit size (%s) and components.\n"),
submir_typestr, sizestr);
free(sizestr);
error = -1;
}
if (error == 0) {
item = dlist_new_item((void *)submir);
if (item == NULL) {
error = ENOMEM;
} else {
*submirs_with_size =
dlist_append(item, *submirs_with_size, AT_TAIL);
}
}
}
return (error);
}
/*
* FUNCTION: validate_slice_components(devconfig_t *req,
* component_type_t type)
*
* INPUT: req - the request
* type - the type of volume being requested
*
* SIDEEFFECT: if the slice component is otherwise valid, an attempt is made
* to reserve it.
*
* RETURNS: int - 0 if the request passes slice component validation
* !0 otherwise.
*
* PURPOSE: Validation function for a concat, stripe or HSP request's
* explicitly specified slice components.
*
* Is the component slice a known device
* Is the component slice available
* Is the component slice already reserved
*
* If the request is for a stripe or concat and the
* request specifies an explicit size, it cannot also
* specify component slices. This is a validation failure.
*
* If the request is for a stripe, the number of specified
* slice components must agree with any expilcit specification
* of the minimum or maximum number of components the stripe
* should have.
*/
static int
validate_slice_components(
devconfig_t *req,
component_type_t type)
{
dlist_t *list = NULL;
dlist_t *iter = NULL;
int error = 0;
int ncomp = 0;
char *dsname = get_request_diskset();
char *voltype = devconfig_type_to_str(type);
list = devconfig_get_components(req);
for (iter = list; (iter != NULL) && (error == 0); iter = iter->next) {
devconfig_t *comp = (devconfig_t *)iter->obj;
component_type_t ctype = TYPE_UNKNOWN;
char *cname = NULL;
dm_descriptor_t slice = (dm_descriptor_t)0;
if ((error = devconfig_get_type(comp, &ctype)) != 0) {
volume_set_error(
gettext("error getting requested component type."),
voltype);
continue;
}
if ((error = devconfig_get_name(comp, &cname)) != 0) {
volume_set_error(
gettext("error getting requested component name."));
continue;
}
if (cname == NULL || cname[0] == '\0') {
volume_set_error(
gettext("%s requested component has no name."),
voltype);
error = -1;
continue;
}
if (ctype == TYPE_SLICE) {
boolean_t in_set = B_FALSE;
boolean_t is_avail = B_FALSE;
boolean_t is_rsvd = B_FALSE;
dm_descriptor_t disk = (dm_descriptor_t)0;
/* is the slice known and explicitly available? */
if ((error = slice_is_available(cname, req,
&is_avail)) != 0) {
if (error == ENODEV) {
volume_set_error(
gettext("%s requested component does not "
"exist: \"%s\"."),
voltype, cname);
error = -1;
}
continue;
}
if (is_avail != B_TRUE) {
volume_set_error(
gettext("%s requested component is "
"unavailable: \"%s\"."),
voltype, cname);
error = -1;
continue;
}
/* get slice and its disk */
((error = slice_get_by_name(cname, &slice)) != 0) ||
(error = slice_get_disk(slice, &disk)) ||
(error = is_reserved_slice(slice, &is_rsvd)) ||
(error = is_disk_in_diskset(disk, dsname, &in_set));
if (error != 0) {
continue;
}
/* is disk in the set? */
if (in_set != B_TRUE) {
volume_set_error(
gettext("%s specifies a component not in "
"disk set \"%s\": \"%s\"."),
voltype, dsname, cname);
error = -1;
continue;
}
/* was slice specified in some other request? */
if (is_rsvd == B_TRUE) {
/* include aliases in the error */
char *aliases =
get_device_aliases_string((dm_descriptor_t)slice);
if (aliases[0] != NULL) {
volume_set_error(
gettext("%s specifies a previously used "
"component: \"%s\" "
"(aliases: %s).\n"),
voltype, cname, aliases);
} else {
volume_set_error(
gettext("%s specifies a previously used "
"component: \"%s\"\n"),
voltype, cname);
}
error = -1;
continue;
}
/* component is ok, reserve it */
error = add_reserved_slice(slice);
/*
* the reserved slice component still needs to be
* checked against slices in use by SVM, but that
* information isn't available yet: the usable
* slice derivation happens after validation.
*
* validate_reserved_slices() can be used to check
* them once the usable slices are determined.
*/
} else {
volume_set_error(
gettext("%s requested component has illegal type."),
voltype);
error = -1;
continue;
}
}
if (error != 0) {
return (error);
}
ncomp = dlist_length(list);
if ((ncomp > 0) && (type == TYPE_CONCAT || type == TYPE_STRIPE)) {
/* explicit size requested for the stripe/concat? */
uint64_t size = 0;
if ((error = devconfig_get_size(req, &size)) != 0) {
if (error == ERR_ATTR_UNSET) {
error = 0;
}
} else {
/* size and components both specified */
char *sizestr = NULL;
(void) bytes_to_sizestr(
size, &sizestr, universal_units, B_FALSE);
volume_set_error(
gettext("%s specifies both an explicit size (%s) "
"and components."),
voltype, sizestr);
free(sizestr);
error = -1;
}
}
if (error != 0) {
return (error);
}
if ((ncomp > 0) && (type == TYPE_STRIPE)) {
/* does # of components agree with min & max comps? */
uint16_t min = 0;
uint16_t max = 0;
if ((error = devconfig_get_stripe_mincomp(req, &min)) != 0) {
if (error == ERR_ATTR_UNSET) {
/* min comp not requested */
error = 0;
} else {
/* error getting requested mincomp */
return (error);
}
} else if (ncomp < min) {
/* specified comps < requested mincomp */
volume_set_error(
gettext("%s specifies fewer components (%d) than the "
"minimum number requested (%d).\n"),
voltype, ncomp, min);
error = -1;
return (error);
}
if ((error = devconfig_get_stripe_maxcomp(req, &max)) != 0) {
if (error == ERR_ATTR_UNSET) {
/* max comp not requested */
error = 0;
} else {
/* error getting request maxcomp */
return (error);
}
} else if (ncomp > max) {
/* specified comps > requested maxcomp */
volume_set_error(
gettext("%s specifies more components (%d) than the "
"maximum number requested (%d).\n"),
voltype, ncomp, max);
error = -1;
return (error);
}
}
return (error);
}
/*
* Generate a list of known aliases for the input descriptor.
*
* The returned string buffer is in the form: "alias1", "alias2"...
*/
static char *
get_device_aliases_string(
dm_descriptor_t desc)
{
static char buf[BUFSIZ];
dlist_t *aliases = NULL;
dlist_t *iter = NULL;
buf[0] = '\0';
(void) get_aliases(desc, &aliases);
for (iter = aliases; iter != NULL; iter = iter->next) {
if (*buf == '\0') {
(void) snprintf(buf, BUFSIZ-1, "\"%s\"", (char *)iter->obj);
} else {
char tmp[BUFSIZ];
(void) strcpy(buf, tmp);
(void) snprintf(buf, BUFSIZ-1, "%s, \"%s\"",
tmp, (char *)iter->obj);
}
}
dlist_free_items(aliases, free);
return (buf);
}