1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <limits.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <thread.h>
33 #include <time.h>
34 #include <unistd.h>
35 
36 #include <sys/mman.h>
37 #include <sys/stat.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #include <sys/utsname.h>
41 
42 #include <libxml/debugXML.h>
43 #include <libxml/parser.h>
44 #include <libxml/tree.h>
45 #include <libxml/xmlerror.h>
46 #include <libxml/xpath.h>
47 #include <libxml/xmlmemory.h>
48 
49 #include <pool.h>
50 #include "pool_internal.h"
51 #include "pool_impl.h"
52 #include "pool_xml_impl.h"
53 
54 /*
55  * libpool XML Manipulation Routines
56  *
57  * pool_xml.c implements the XML manipulation routines used by the libpool
58  * XML datastore. The functions are grouped into the following logical areas
59  * - Result Sets
60  * The XPath API is used to search the XML document represented by a
61  * configuration. The results of XPath queries are represented through
62  * pool_result_set_t structures as part of the abstraction of the datastore
63  * representation. (see pool.c comment for more details)
64  *
65  * - Property Manipulation
66  * Validated XML (XML associated with a DTD) does not allow the introduction
67  * of attributes which are not recognised by the DTD. This is a limitation
68  * since we want to allow libpool to associate an arbitrary number of
69  * properties with an element. The property manipulation code overcomes this
70  * limitation by allowing property sub-elements to be created and manipulated
71  * through a single API so that they are indistinguishable from attributes
72  * to the libpool user.
73  *
74  * - XML Element/Attribute Manipulation
75  * These routines manipulate XML elements and attributes and are the routines
76  * which interact most directly with libxml.
77  *
78  * - File Processing/IO
79  * Since libpool must present its data in a consistent fashion, we have to
80  * implement file locking above libxml. These routines allow us to lock files
81  * during processing and maintain data integrity between processes. Note
82  * that locks are at the process scope and are advisory (see fcntl).
83  *
84  * - Utilities
85  * Sundry utility functions that aren't easily categorised.
86  */
87 
88 #define	MAX_PROP_SIZE	1024	/* Size of property buffer */
89 /*
90  * The PAGE_READ_SIZE value is used to determine the size of the input buffer
91  * used to parse XML files.
92  */
93 #define	PAGE_READ_SIZE	8192
94 #define	ELEM_TYPE_COUNT	6	/* Count of Element types */
95 
96 typedef struct dtype_tbl
97 {
98 	xmlChar *dt_name;
99 	int dt_type;
100 } dtype_tbl_t;
101 
102 typedef struct elem_type_tbl
103 {
104 	xmlChar *ett_elem;
105 	dtype_tbl_t (*ett_dtype)[];
106 } elem_type_tbl_t;
107 
108 extern int xmlDoValidityCheckingDefaultValue;
109 
110 /*
111  * The _xml_lock is used to lock the state of libpool during
112  * xml initialisation operations.
113  */
114 static mutex_t _xml_lock;
115 
116 const char *element_class_tags[] = {
117 	"any",
118 	"system",
119 	"pool",
120 	"res_comp",
121 	"res_agg",
122 	"comp",
123 	NULL
124 };
125 
126 static const char *data_type_tags[] = {
127 	"uint",
128 	"int",
129 	"float",
130 	"boolean",
131 	"string"
132 };
133 
134 const char *dtd_location = "file:///usr/share/lib/xml/dtd/rm_pool.dtd.1";
135 
136 static elem_type_tbl_t elem_tbl[ELEM_TYPE_COUNT] = {0};
137 
138 /* libpool initialisation indicator */
139 static int _libpool_xml_initialised = PO_FALSE;
140 
141 /*
142  * Utility functions
143  */
144 /*
145  * Those functions which are not static are shared with pool_kernel.c
146  * They provide the required XML support for exporting a kernel
147  * configuration as an XML document.
148  */
149 void xml_init(void);
150 static int create_shadow(xmlNodePtr node);
151 static int pool_xml_free_doc(pool_conf_t *conf);
152 static int prop_sort(const void *a, const void *b);
153 static int dtd_exists(const char *path);
154 static void build_dtype_accelerator(void);
155 static dtype_tbl_t (*build_dtype_tbl(const xmlChar *rawdata))[];
156 static int get_fast_dtype(xmlNodePtr node, xmlChar *name);
157 static int pool_assoc_default_resource_type(pool_t *,
158     pool_resource_elem_class_t);
159 
160 /*
161  * XML Data access and navigation APIs
162  */
163 static int pool_build_xpath_buf(pool_xml_connection_t *, const pool_elem_t *,
164     pool_elem_class_t, pool_value_t **, char_buf_t *, int);
165 /*
166  * SHARED WITH pool_kernel.c for XML export support
167  */
168 xmlNodePtr node_create(xmlNodePtr parent, const xmlChar *name);
169 static xmlNodePtr node_create_with_id(xmlNodePtr parent, const xmlChar *name);
170 
171 /* Configuration */
172 static int pool_xml_close(pool_conf_t *);
173 static int pool_xml_validate(const pool_conf_t *, pool_valid_level_t);
174 static int pool_xml_commit(pool_conf_t *conf);
175 static int pool_xml_export(const pool_conf_t *conf, const char *location,
176     pool_export_format_t fmt);
177 static int pool_xml_rollback(pool_conf_t *conf);
178 static pool_result_set_t *pool_xml_exec_query(const pool_conf_t *conf,
179     const pool_elem_t *src, const char *src_attr,
180     pool_elem_class_t classes, pool_value_t **props);
181 static int pool_xml_remove(pool_conf_t *conf);
182 static int pool_xml_res_transfer(pool_resource_t *, pool_resource_t *,
183     uint64_t);
184 static int pool_xml_res_xtransfer(pool_resource_t *, pool_resource_t *,
185     pool_component_t **);
186 
187 /* Connections */
188 static void pool_xml_connection_free(pool_xml_connection_t *prov);
189 
190 /* Result Sets */
191 static pool_xml_result_set_t *pool_xml_result_set_alloc(const pool_conf_t *);
192 static void pool_xml_result_set_free(pool_xml_result_set_t *rs);
193 static pool_elem_t *pool_xml_rs_next(pool_result_set_t *set);
194 static pool_elem_t *pool_xml_rs_prev(pool_result_set_t *set);
195 static pool_elem_t *pool_xml_rs_first(pool_result_set_t *set);
196 static pool_elem_t *pool_xml_rs_last(pool_result_set_t *set);
197 static int pool_xml_rs_set_index(pool_result_set_t *set, int index);
198 static int pool_xml_rs_get_index(pool_result_set_t *set);
199 static int pool_xml_rs_count(pool_result_set_t *set);
200 static int pool_xml_rs_close(pool_result_set_t *set);
201 
202 /* Element (and sub-type) */
203 static void pool_xml_elem_init(pool_conf_t *conf, pool_xml_elem_t *elem,
204     pool_elem_class_t, pool_resource_elem_class_t, pool_component_elem_class_t);
205 static int pool_xml_elem_wrap(xmlNodePtr node, pool_elem_class_t class,
206     pool_resource_elem_class_t, pool_component_elem_class_t);
207 static pool_elem_t *pool_xml_elem_create(pool_conf_t *, pool_elem_class_t,
208     pool_resource_elem_class_t, pool_component_elem_class_t);
209 static int pool_xml_elem_remove(pool_elem_t *pe);
210 static int pool_xml_set_container(pool_elem_t *, pool_elem_t *);
211 static pool_elem_t *pool_xml_get_container(const pool_elem_t *);
212 
213 /*
214  * Pool element specific
215  */
216 static int pool_xml_pool_associate(pool_t *, const pool_resource_t *);
217 static int pool_xml_pool_dissociate(pool_t *, const pool_resource_t *);
218 
219 /*
220  * Resource elements specific
221  */
222 static int pool_xml_resource_is_system(const pool_resource_t *);
223 static int pool_xml_resource_can_associate(const pool_resource_t *);
224 
225 /* Properties */
226 static pool_value_class_t pool_xml_get_property(const pool_elem_t *,
227     const char *, pool_value_t *);
228 static int pool_xml_put_property(pool_elem_t *, const char *,
229     const pool_value_t *);
230 static int pool_xml_rm_property(pool_elem_t *, const char *);
231 static xmlNodePtr property_create(xmlNodePtr, const char *,
232     pool_value_class_t);
233 
234 /* Internal Attribute/Property manipulation */
235 static int pool_is_xml_attr(xmlDocPtr, const char *, const char *);
236 static pool_value_class_t pool_xml_get_attr(xmlNodePtr node, xmlChar *name,
237     pool_value_t *value);
238 int pool_xml_set_attr(xmlNodePtr node, xmlChar *name,
239     const pool_value_t *value);
240 static pool_value_class_t pool_xml_get_prop(xmlNodePtr node, xmlChar *name,
241     pool_value_t *value);
242 int pool_xml_set_prop(xmlNodePtr node, xmlChar *name,
243     const pool_value_t *value);
244 static pool_value_t **pool_xml_get_properties(const pool_elem_t *, uint_t *);
245 /* XML Error handling */
246 void pool_error_func(void *ctx, const char *msg, ...);
247 
248 /* XML File Input Processing */
249 static int pool_xml_open_file(pool_conf_t *conf);
250 static int pool_xml_parse_document(pool_conf_t *);
251 
252 /*
253  * Initialise this module
254  */
255 void
xml_init()256 xml_init()
257 {
258 	(void) mutex_lock(&_xml_lock);
259 	if (_libpool_xml_initialised == PO_TRUE) {
260 		(void) mutex_unlock(&_xml_lock);
261 		return;
262 	}
263 	xmlInitParser();
264 
265 	/* Send all XML errors to our debug handler */
266 	xmlSetGenericErrorFunc(NULL, pool_error_func);
267 	/* Load up DTD element a-dtype data to improve performance */
268 	build_dtype_accelerator();
269 	_libpool_xml_initialised = PO_TRUE;
270 	(void) mutex_unlock(&_xml_lock);
271 }
272 
273 /*
274  * Get the next ID for this configuration
275  */
276 static int
get_unique_id(xmlNodePtr node,char * id)277 get_unique_id(xmlNodePtr node, char *id)
278 {
279 	pool_value_t val = POOL_VALUE_INITIALIZER;
280 	uint64_t nid = 0;
281 	if (node->doc->_private) {
282 		if (pool_get_ns_property(
283 		    pool_conf_to_elem((pool_conf_t *)node->doc->_private),
284 		    "_next_id", &val) == POC_UINT)
285 			(void) pool_value_get_uint64(&val, &nid);
286 	}
287 	if (snprintf(id, KEY_BUFFER_SIZE, "id_%llx", nid) > KEY_BUFFER_SIZE) {
288 		pool_seterror(POE_SYSTEM);
289 		return (PO_FAIL);
290 	}
291 	pool_value_set_uint64(&val, ++nid);
292 	return (pool_put_ns_property(
293 	    pool_conf_to_elem((pool_conf_t *)node->doc->_private), "_next_id",
294 	    &val));
295 }
296 
297 /* Document building functions */
298 
299 /*
300  * node_create() creates a child node of type name of the supplied parent in
301  * the supplied document. If the parent or document is NULL, create the node
302  * but do not associate it with a parent or document.
303  */
304 xmlNodePtr
node_create(xmlNodePtr parent,const xmlChar * name)305 node_create(xmlNodePtr parent, const xmlChar *name)
306 {
307 	xmlNodePtr node;
308 
309 	if (parent == NULL)
310 		node = xmlNewNode(NULL, name);
311 	else
312 		node = xmlNewChild(parent, NULL, name, NULL);
313 	return (node);
314 }
315 
316 /*
317  * node_create_with_id() creates a child node of type name of the supplied
318  * parent with the ref_id generated by get_unique_id(). Actual node creation
319  * is performed by node_create() and this function just sets the ref_id
320  * property to the value of the id.
321  */
322 static xmlNodePtr
node_create_with_id(xmlNodePtr parent,const xmlChar * name)323 node_create_with_id(xmlNodePtr parent, const xmlChar *name)
324 {
325 	char id[KEY_BUFFER_SIZE]; /* Must be big enough for key below */
326 	xmlNodePtr node = node_create(parent, name);
327 	if (node != NULL) {
328 		if (get_unique_id(node, id) != PO_SUCCESS) {
329 			xmlUnlinkNode(node);
330 			xmlFreeNode(node); /* recurses all children */
331 			pool_seterror(POE_DATASTORE);
332 			return (NULL);
333 		}
334 		if (xmlSetProp(node, BAD_CAST c_ref_id, BAD_CAST id) == NULL) {
335 			xmlUnlinkNode(node);
336 			xmlFreeNode(node); /* recurses all children */
337 			pool_seterror(POE_DATASTORE);
338 			return (NULL);
339 		}
340 	}
341 	return (node);
342 }
343 
344 /* Supporting Data Conversion Routines */
345 
346 /* XML Parser Utility Functions */
347 
348 /*
349  * Handler for XML Errors. Called by libxml at libxml Error.
350  */
351 /*ARGSUSED*/
352 void
pool_error_func(void * ctx,const char * msg,...)353 pool_error_func(void *ctx, const char *msg, ...)
354 {
355 	va_list ap;
356 
357 	va_start(ap, msg);
358 	do_dprintf(msg, ap);
359 	va_end(ap);
360 }
361 
362 /*
363  * Free the shadowed elements from within the supplied document and then
364  * free the document. This function should always be called when freeing
365  * a pool document to ensure that all "shadow" resources are reclaimed.
366  * Returns PO_SUCCESS/PO_FAIL
367  */
368 static int
pool_xml_free_doc(pool_conf_t * conf)369 pool_xml_free_doc(pool_conf_t *conf)
370 {
371 	/* Only do any of this if there is a document */
372 	if (((pool_xml_connection_t *)conf->pc_prov)->pxc_doc != NULL) {
373 		pool_elem_t *pe;
374 		pool_result_set_t *rs;
375 		/* Delete all the "shadowed" children of the doc */
376 		rs = pool_exec_query(conf, NULL, NULL, PEC_QRY_ANY, NULL);
377 		if (rs == NULL) {
378 			pool_seterror(POE_INVALID_CONF);
379 			return (PO_FAIL);
380 		}
381 		for (pe = rs->prs_next(rs); pe != NULL; pe = rs->prs_next(rs)) {
382 			/*
383 			 * Work out the element type and free the elem
384 			 */
385 			free(pe);
386 		}
387 		(void) pool_rs_close(rs);
388 		xmlFreeDoc(((pool_xml_connection_t *)conf->pc_prov)->pxc_doc);
389 	}
390 	((pool_xml_connection_t *)conf->pc_prov)->pxc_doc = NULL;
391 	return (PO_SUCCESS);
392 }
393 
394 /*
395  * Remove an element from the document. Note that only three types of elements
396  * can be removed, res, comp and pools. comp are moved around to the
397  * default res when a res is deleted.
398  * Returns PO_SUCCESS/PO_FAIL
399  */
400 static int
pool_xml_elem_remove(pool_elem_t * pe)401 pool_xml_elem_remove(pool_elem_t *pe)
402 {
403 	pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe;
404 
405 	/*
406 	 * You can only destroy three elements: pools, resources and
407 	 * components.
408 	 */
409 	switch (pe->pe_class) {
410 	case PEC_POOL:
411 	case PEC_RES_COMP:
412 	case PEC_RES_AGG:
413 	case PEC_COMP:
414 		if (pxe->pxe_node) {
415 			xmlUnlinkNode(pxe->pxe_node);
416 			xmlFreeNode(pxe->pxe_node); /* recurses all children */
417 		}
418 		free(pxe);
419 		break;
420 	default:
421 		break;
422 	}
423 	return (PO_SUCCESS);
424 }
425 
426 /*
427  * Create a property element.
428  */
429 static xmlNodePtr
property_create(xmlNodePtr parent,const char * name,pool_value_class_t type)430 property_create(xmlNodePtr parent, const char *name, pool_value_class_t type)
431 {
432 
433 	xmlNodePtr element;
434 	pool_value_t val = POOL_VALUE_INITIALIZER;
435 
436 	if ((element = node_create(parent, BAD_CAST "property")) == NULL) {
437 		pool_seterror(POE_DATASTORE);
438 		return (NULL);
439 	}
440 	if (pool_value_set_string(&val, name) != PO_SUCCESS) {
441 		xmlFree(element);
442 		return (NULL);
443 	}
444 	(void) pool_xml_set_attr(element, BAD_CAST c_name, &val);
445 	if (pool_value_set_string(&val, data_type_tags[type]) != PO_SUCCESS) {
446 		xmlFree(element);
447 		return (NULL);
448 	}
449 	(void) pool_xml_set_attr(element, BAD_CAST c_type, &val);
450 	return (element);
451 }
452 
453 /*
454  * External clients need to be able to put/get properties and this is the
455  * way to do it.
456  * This function is an interceptor, since it will *always* try to manipulate
457  * an attribute first. If the attribute doesn't exist, then it will treat
458  * the request as a property request.
459  */
460 static pool_value_class_t
pool_xml_get_property(const pool_elem_t * pe,const char * name,pool_value_t * val)461 pool_xml_get_property(const pool_elem_t *pe, const char *name,
462     pool_value_t *val)
463 {
464 	pool_value_class_t type;
465 	pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe;
466 
467 	/*
468 	 * "type" is a special attribute which is not visible ever outside of
469 	 * libpool. Use the specific type accessor function.
470 	 */
471 	if (strcmp(name, c_type) == 0) {
472 		return (pool_xml_get_attr(pxe->pxe_node, BAD_CAST name,
473 		    val));
474 	}
475 	if (is_ns_property(pe, name) != NULL) {	/* in ns */
476 		if ((type = pool_xml_get_attr(pxe->pxe_node,
477 		    BAD_CAST property_name_minus_ns(pe, name), val))
478 		    == POC_INVAL)
479 			return (pool_xml_get_prop(pxe->pxe_node, BAD_CAST name,
480 			    val));
481 	} else
482 		return (pool_xml_get_prop(pxe->pxe_node, BAD_CAST name, val));
483 
484 	return (type);
485 }
486 
487 /*
488  * Put a property on an element. Check if the property is an attribute,
489  * if it is update that value. If not add a property element.
490  *
491  * There are three possible conditions here:
492  * - the name is a ns
493  *	- the name is an attribute
494  *	- the name isn't an attribute
495  * - the name is not a ns
496  * Returns PO_SUCCESS/PO_FAIL
497  */
498 static int
pool_xml_put_property(pool_elem_t * pe,const char * name,const pool_value_t * val)499 pool_xml_put_property(pool_elem_t *pe, const char *name,
500     const pool_value_t *val)
501 {
502 	pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe;
503 
504 	/*
505 	 * "type" is a special attribute which is not visible ever outside of
506 	 * libpool. Use the specific type accessor function.
507 	 */
508 	if (strcmp(name, c_type) == 0) {
509 		return (pool_xml_set_attr(pxe->pxe_node, BAD_CAST name,
510 		    val));
511 	}
512 	if (is_ns_property(pe, name) != NULL) {	/* in ns */
513 		if (pool_xml_set_attr(pxe->pxe_node,
514 		    BAD_CAST property_name_minus_ns(pe, name), val) == PO_FAIL)
515 			return (pool_xml_set_prop(pxe->pxe_node, BAD_CAST name,
516 			    val));
517 	} else
518 		return (pool_xml_set_prop(pxe->pxe_node, BAD_CAST name, val));
519 	return (PO_SUCCESS);
520 }
521 
522 /*
523  * Remove a property from an element. Check if the property is an attribute,
524  * if it is fail. Otherwise remove the property subelement.
525  * Returns PO_SUCCESS/PO_FAIL
526  */
527 static int
pool_xml_rm_property(pool_elem_t * pe,const char * name)528 pool_xml_rm_property(pool_elem_t *pe, const char *name)
529 {
530 	pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe;
531 	xmlXPathContextPtr ctx;
532 	xmlXPathObjectPtr path;
533 	char buf[MAX_PROP_SIZE];
534 	int ret;
535 
536 	if (xmlHasProp(pxe->pxe_node, BAD_CAST name) != NULL) {
537 		pool_seterror(POE_BADPARAM);
538 		return (PO_FAIL);
539 	}
540 
541 	/* use xpath to find the node with the appropriate value for name */
542 	(void) snprintf(buf, sizeof (buf), "property[@name=\"%s\"]", name);
543 	if ((ctx = xmlXPathNewContext(pxe->pxe_node->doc)) == NULL) {
544 		pool_seterror(POE_PUTPROP);
545 		return (PO_FAIL);
546 	}
547 	ctx->node = pxe->pxe_node;
548 	path = xmlXPathEval(BAD_CAST buf, ctx);
549 
550 	if (path && (path->type == XPATH_NODESET) &&
551 	    (path->nodesetval->nodeNr == 1)) {
552 		xmlUnlinkNode(path->nodesetval->nodeTab[0]);
553 		xmlFreeNode(path->nodesetval->nodeTab[0]);
554 		ret = PO_SUCCESS;
555 	} else {
556 		pool_seterror(POE_BADPARAM);
557 		ret = PO_FAIL;
558 	}
559 	xmlXPathFreeObject(path);
560 	xmlXPathFreeContext(ctx);
561 	return (ret);
562 }
563 
564 /*
565  * Get the data type for an attribute name from the element node. The data
566  * type is returned and the value of the attribute updates the supplied value
567  * pointer.
568  */
569 static pool_value_class_t
pool_xml_get_attr(xmlNodePtr node,xmlChar * name,pool_value_t * value)570 pool_xml_get_attr(xmlNodePtr node, xmlChar *name, pool_value_t *value)
571 {
572 	pool_value_class_t data_type;
573 	xmlChar *data;
574 	uint64_t uval;
575 	int64_t ival;
576 
577 	if (xmlHasProp(node, name) == NULL && pool_is_xml_attr(node->doc,
578 	    (const char *) node->name, (const char *) name) == PO_FALSE) {
579 		pool_seterror(POE_BADPARAM);
580 		return (POC_INVAL);
581 	}
582 	if (xmlHasProp(node, BAD_CAST c_a_dtype) == NULL) {
583 		pool_seterror(POE_INVALID_CONF);
584 		return (POC_INVAL);
585 	}
586 	data = xmlGetProp(node, name);
587 	data_type = get_fast_dtype(node, name);
588 	if (data_type != POC_STRING && data == NULL) {
589 		pool_seterror(POE_INVALID_CONF);
590 		return (POC_INVAL);
591 	}
592 	switch (data_type) {
593 	case POC_UINT:
594 		errno = 0;
595 		uval = strtoull((char *)data, NULL, 0);
596 		if (errno != 0) {
597 			data_type =  POC_INVAL;
598 		}
599 		else
600 			pool_value_set_uint64(value, uval);
601 		break;
602 	case POC_INT:
603 		errno = 0;
604 		ival = strtoll((char *)data, NULL, 0);
605 		if (errno != 0) {
606 			data_type =  POC_INVAL;
607 		}
608 		else
609 			pool_value_set_int64(value, ival);
610 		break;
611 	case POC_DOUBLE:
612 		pool_value_set_double(value, atof((const char *)data));
613 		break;
614 	case POC_BOOL:
615 		if (strcmp((const char *)data, "true") == 0)
616 			pool_value_set_bool(value, PO_TRUE);
617 		else
618 			pool_value_set_bool(value, PO_FALSE);
619 		break;
620 	case POC_STRING:
621 		if (pool_value_set_string(value, data ?
622 		    (const char *)data : "") != PO_SUCCESS) {
623 			xmlFree(data);
624 			return (POC_INVAL);
625 		}
626 		break;
627 	case POC_INVAL:
628 	default:
629 		break;
630 	}
631 	xmlFree(data);
632 	return (data_type);
633 }
634 
635 /*
636  * Set the data type for an attribute name from the element node. The
637  * supplied value is used to update the designated name using the data
638  * type supplied.
639  */
640 int
pool_xml_set_attr(xmlNodePtr node,xmlChar * name,const pool_value_t * value)641 pool_xml_set_attr(xmlNodePtr node, xmlChar *name, const pool_value_t *value)
642 {
643 	xmlChar buf[MAX_PROP_SIZE] = {0};
644 	uint64_t ures;
645 	int64_t ires;
646 	uchar_t bres;
647 	double dres;
648 	const char *sres;
649 
650 	pool_value_class_t data_type;
651 
652 	if (xmlHasProp(node, name) == NULL && pool_is_xml_attr(node->doc,
653 	    (const char *) node->name, (const char *) name) == PO_FALSE) {
654 		pool_seterror(POE_BADPARAM);
655 		return (PO_FAIL);
656 	}
657 
658 	if (xmlHasProp(node, BAD_CAST c_a_dtype) == NULL) {
659 		pool_seterror(POE_INVALID_CONF);
660 		return (PO_FAIL);
661 	}
662 	data_type = get_fast_dtype(node, name);
663 	if (data_type != value->pv_class) {
664 		pool_seterror(POE_BADPARAM);
665 		return (PO_FAIL);
666 	}
667 	switch (value->pv_class) {
668 	case POC_UINT:
669 		(void) pool_value_get_uint64(value, &ures);
670 		(void) snprintf((char *)buf, sizeof (buf), "%llu",
671 		    (u_longlong_t)ures);
672 		break;
673 	case POC_INT:
674 		(void) pool_value_get_int64(value, &ires);
675 		(void) snprintf((char *)buf, sizeof (buf), "%lld",
676 		    (longlong_t)ires);
677 		break;
678 	case POC_DOUBLE:
679 		(void) pool_value_get_double(value, &dres);
680 		(void) snprintf((char *)buf, sizeof (buf), "%f", dres);
681 		break;
682 	case POC_BOOL:
683 		(void) pool_value_get_bool(value, &bres);
684 		if (bres == PO_FALSE)
685 			(void) snprintf((char *)buf, sizeof (buf),
686 			    "false");
687 		else
688 			(void) snprintf((char *)buf, sizeof (buf),
689 			    "true");
690 		break;
691 	case POC_STRING:
692 		(void) pool_value_get_string(value, &sres);
693 		if (sres != NULL)
694 			(void) snprintf((char *)buf, sizeof (buf), "%s",
695 			    sres);
696 		break;
697 	case POC_INVAL:
698 	default:
699 		break;
700 	}
701 	if (xmlSetProp(node, name, buf) == NULL) {
702 		pool_seterror(POE_DATASTORE);
703 		return (PO_FAIL);
704 	}
705 	return (PO_SUCCESS);
706 }
707 
708 /*
709  * Get the data type for a property name from the element node. The data
710  * type is returned and the value of the property updates the supplied value
711  * pointer. The user is responsible for freeing the memory associated with
712  * a string.
713  */
714 static pool_value_class_t
pool_xml_get_prop(xmlNodePtr node,xmlChar * name,pool_value_t * value)715 pool_xml_get_prop(xmlNodePtr node, xmlChar *name, pool_value_t *value)
716 {
717 	pool_value_class_t data_type;
718 	xmlChar *data, *node_data;
719 	xmlXPathContextPtr ctx;
720 	xmlXPathObjectPtr path;
721 	char buf[MAX_PROP_SIZE];
722 	int64_t uval;
723 	int64_t ival;
724 
725 	/* use xpath to find the node with the appropriate value for name */
726 	(void) snprintf(buf, sizeof (buf), "property[@name=\"%s\"]", name);
727 	if ((ctx = xmlXPathNewContext(node->doc)) == NULL) {
728 		pool_seterror(POE_BADPARAM);
729 		return (POC_INVAL);
730 	}
731 	ctx->node = node;
732 	path = xmlXPathEval(BAD_CAST buf, ctx);
733 
734 	if (path && (path->type == XPATH_NODESET) &&
735 	    (path->nodesetval->nodeNr == 1)) {
736 		int i;
737 		if (xmlHasProp(path->nodesetval->nodeTab[0],
738 		    BAD_CAST c_type) == NULL) {
739 			xmlXPathFreeObject(path);
740 			xmlXPathFreeContext(ctx);
741 			pool_seterror(POE_INVALID_CONF);
742 			return (POC_INVAL);
743 		}
744 		/* type is a string representation of the type */
745 		data = xmlGetProp(path->nodesetval->nodeTab[0],
746 		    BAD_CAST c_type);
747 		node_data = xmlNodeGetContent(path->nodesetval->nodeTab[0]);
748 		data_type = POC_INVAL;
749 		for (i = 0; i < (sizeof (data_type_tags) /
750 		    sizeof (data_type_tags[0])); i++) {
751 			if (strcmp((char *)data, data_type_tags[i]) == 0) {
752 				data_type = i;
753 				break;
754 			}
755 		}
756 		switch (data_type) {
757 		case POC_UINT:
758 			errno = 0;
759 			uval = strtoull((char *)node_data, NULL, 0);
760 			if (errno != 0)
761 				data_type =  POC_INVAL;
762 			else
763 				pool_value_set_uint64(value, uval);
764 			break;
765 		case POC_INT:
766 			errno = 0;
767 			ival = strtoll((char *)node_data, NULL, 0);
768 			if (errno != 0)
769 				data_type =  POC_INVAL;
770 			else
771 				pool_value_set_int64(value, ival);
772 			break;
773 		case POC_DOUBLE:
774 			pool_value_set_double(value,
775 			    atof((const char *)node_data));
776 			break;
777 		case POC_BOOL:
778 			if (strcmp((const char *)node_data, "true")
779 			    == 0)
780 				pool_value_set_bool(value, PO_TRUE);
781 			else
782 				pool_value_set_bool(value, PO_FALSE);
783 			break;
784 		case POC_STRING:
785 			if (pool_value_set_string(value,
786 			    (const char *)node_data) != PO_SUCCESS) {
787 				data_type = POC_INVAL;
788 				break;
789 			}
790 			break;
791 		case POC_INVAL:
792 		default:
793 			break;
794 		}
795 		xmlFree(data);
796 		xmlFree(node_data);
797 		xmlXPathFreeObject(path);
798 		xmlXPathFreeContext(ctx);
799 		return (data_type);
800 	} else { /* No property exists, clean up and return */
801 		xmlXPathFreeObject(path);
802 		xmlXPathFreeContext(ctx);
803 		pool_seterror(POE_BADPARAM);
804 		return (POC_INVAL);
805 	}
806 }
807 
808 /*
809  * Set the data type for a property name from the element node. The
810  * supplied value is used to update the designated name using the data
811  * type supplied.
812  */
813 int
pool_xml_set_prop(xmlNodePtr node,xmlChar * name,const pool_value_t * value)814 pool_xml_set_prop(xmlNodePtr node, xmlChar *name, const pool_value_t *value)
815 {
816 /* First check if we have a property with this name (and type???). */
817 	xmlXPathContextPtr ctx;
818 	xmlXPathObjectPtr path;
819 	xmlChar buf[MAX_PROP_SIZE];
820 	xmlNodePtr element;
821 	uint64_t ures;
822 	int64_t ires;
823 	uchar_t bres;
824 	double dres;
825 	const char *sres;
826 
827 	/* use xpath to find the node with the appropriate value for name */
828 	(void) snprintf((char *)buf, sizeof (buf), "property[@name=\"%s\"]",
829 	    name);
830 	if ((ctx = xmlXPathNewContext(node->doc)) == NULL) {
831 		pool_seterror(POE_BADPARAM);
832 		return (PO_FAIL);
833 	}
834 	ctx->node = node;
835 	path = xmlXPathEval(buf, ctx);
836 	if (path == NULL || path->type != XPATH_NODESET) {
837 		xmlXPathFreeObject(path);
838 		xmlXPathFreeContext(ctx);
839 		pool_seterror(POE_BADPARAM);
840 		return (PO_FAIL);
841 	} else {
842 		if (path->nodesetval->nodeNr == 0)
843 			element = property_create
844 			    (node, (const char *)name, value->pv_class);
845 		else if (path->nodesetval->nodeNr == 1) {
846 			int i;
847 			xmlChar *data;
848 
849 			element = path->nodesetval->nodeTab[0];
850 			if (xmlHasProp(element, BAD_CAST c_type) == NULL) {
851 				xmlXPathFreeObject(path);
852 				xmlXPathFreeContext(ctx);
853 				pool_seterror(POE_INVALID_CONF);
854 				return (PO_FAIL);
855 			}
856 			data = xmlGetProp(element, BAD_CAST c_type);
857 			for (i = 0; i < (sizeof (data_type_tags) /
858 			    sizeof (data_type_tags[0])); i++)
859 				if (strcmp((char *)data, data_type_tags[i])
860 				    == 0) {
861 					break;
862 				}
863 			xmlFree(data);
864 			if (value->pv_class != i) {
865 				xmlXPathFreeObject(path);
866 				xmlXPathFreeContext(ctx);
867 				pool_seterror(POE_BADPARAM);
868 				return (PO_FAIL);
869 			}
870 		} else {
871 			xmlXPathFreeObject(path);
872 			xmlXPathFreeContext(ctx);
873 			pool_seterror(POE_BADPARAM);
874 			return (PO_FAIL);
875 		}
876 	}
877 
878 	switch (value->pv_class) {
879 	case POC_UINT:
880 		(void) pool_value_get_uint64(value, &ures);
881 		(void) snprintf((char *)buf, sizeof (buf), "%llu",
882 		    (u_longlong_t)ures);
883 		break;
884 	case POC_INT:
885 		(void) pool_value_get_int64(value, &ires);
886 		(void) snprintf((char *)buf, sizeof (buf), "%lld",
887 		    (longlong_t)ires);
888 		break;
889 	case POC_DOUBLE:
890 		(void) pool_value_get_double(value, &dres);
891 		(void) snprintf((char *)buf, sizeof (buf), "%f", dres);
892 		break;
893 	case POC_BOOL:
894 		(void) pool_value_get_bool(value, &bres);
895 		if (bres == PO_FALSE)
896 			(void) snprintf((char *)buf, sizeof (buf),
897 			    "false");
898 		else
899 			(void) snprintf((char *)buf, sizeof (buf),
900 			    "true");
901 		break;
902 	case POC_STRING:
903 		(void) pool_value_get_string(value, &sres);
904 		(void) snprintf((char *)buf, sizeof (buf), "%s", sres);
905 		break;
906 	case POC_INVAL:
907 	default:
908 		break;
909 	}
910 	xmlNodeSetContent(element, buf);
911 	xmlXPathFreeObject(path);
912 	xmlXPathFreeContext(ctx);
913 	return (PO_SUCCESS);
914 }
915 
916 /*
917  * Return a NULL terminated array of pool_value_t which represents all
918  * of the properties stored for an element
919  *
920  * Return NULL on failure. It is the caller's responsibility to free
921  * the returned array of values.
922  */
923 pool_value_t **
pool_xml_get_properties(const pool_elem_t * pe,uint_t * nprops)924 pool_xml_get_properties(const pool_elem_t *pe, uint_t *nprops)
925 {
926 	pool_value_t **result;
927 	pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe;
928 	int i, j;
929 	pool_conf_t *conf = TO_CONF(pe);
930 	xmlElementPtr elemDTD;
931 	xmlAttributePtr attr;
932 	xmlXPathContextPtr ctx;
933 	xmlXPathObjectPtr path;
934 	char_buf_t *cb = NULL;
935 
936 	*nprops = 0;
937 
938 	elemDTD = xmlGetDtdElementDesc(pxe->pxe_node->doc->extSubset,
939 	    pxe->pxe_node->name);
940 	for (attr = elemDTD->attributes; attr != NULL; attr = attr->nexth) {
941 		if (strcmp((const char *)attr->name, c_a_dtype) != 0 ||
942 		    strcmp((const char *)attr->name, c_type) != 0)
943 			(*nprops)++;
944 	}
945 	if ((ctx = xmlXPathNewContext(
946 	    ((pool_xml_connection_t *)conf->pc_prov)->pxc_doc)) == NULL) {
947 		pool_seterror(POE_BADPARAM);
948 		return (NULL);
949 	}
950 	ctx->node = pxe->pxe_node;
951 	path = xmlXPathEval(BAD_CAST "property", ctx);
952 
953 	if (path != NULL && path->type == XPATH_NODESET &&
954 	    path->nodesetval != NULL)
955 		(*nprops) += path->nodesetval->nodeNr;
956 
957 	if ((result = calloc(*nprops + 1, sizeof (pool_value_t *))) == NULL) {
958 		xmlXPathFreeObject(path);
959 		xmlXPathFreeContext(ctx);
960 		pool_seterror(POE_SYSTEM);
961 		return (NULL);
962 	}
963 	if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL) {
964 		xmlXPathFreeObject(path);
965 		xmlXPathFreeContext(ctx);
966 		free(result);
967 		return (NULL);
968 	}
969 	/*
970 	 * Now store our attributes and properties in result
971 	 */
972 	for (i = 0, attr = elemDTD->attributes; attr != NULL;
973 	    attr = attr->nexth, i++) {
974 		if (strcmp((const char *)attr->name, c_a_dtype) == 0 ||
975 		    strcmp((const char *)attr->name, c_type) == 0) {
976 			i--;
977 			continue;
978 		}
979 		result[i] = pool_value_alloc();
980 		if (pool_xml_get_attr(pxe->pxe_node,
981 		    BAD_CAST attr->name, result[i]) == POC_INVAL) {
982 			xmlXPathFreeObject(path);
983 			xmlXPathFreeContext(ctx);
984 			while (i-- >= 0)
985 				pool_value_free(result[i]);
986 			free(result);
987 			free_char_buf(cb);
988 			return (NULL);
989 		}
990 		if (strcmp((const char *)attr->name, c_type) != 0) {
991 			if (set_char_buf(cb, "%s.%s",
992 			    pool_elem_class_string(pe), attr->name) !=
993 			    PO_SUCCESS) {
994 				xmlXPathFreeObject(path);
995 				xmlXPathFreeContext(ctx);
996 				while (i-- >= 0)
997 					pool_value_free(result[i]);
998 				free(result);
999 				free_char_buf(cb);
1000 				return (NULL);
1001 			}
1002 			if (pool_value_set_name(result[i], cb->cb_buf) !=
1003 			    PO_SUCCESS) {
1004 				xmlXPathFreeObject(path);
1005 				xmlXPathFreeContext(ctx);
1006 				while (i-- >= 0)
1007 					pool_value_free(result[i]);
1008 				free(result);
1009 				free_char_buf(cb);
1010 				return (NULL);
1011 			}
1012 		} else {
1013 			if (pool_value_set_name(result[i],
1014 			    (const char *)attr->name) != PO_SUCCESS) {
1015 				xmlXPathFreeObject(path);
1016 				xmlXPathFreeContext(ctx);
1017 				while (i-- >= 0)
1018 					pool_value_free(result[i]);
1019 				free(result);
1020 				free_char_buf(cb);
1021 				return (NULL);
1022 			}
1023 		}
1024 	}
1025 	free_char_buf(cb);
1026 	for (j = 0; j < path->nodesetval->nodeNr; j++, i++) {
1027 		xmlChar *name = xmlGetProp(path->nodesetval->nodeTab[j],
1028 		    BAD_CAST c_name);
1029 
1030 		result[i] = pool_value_alloc();
1031 
1032 		if (pool_xml_get_prop(pxe->pxe_node, name, result[i]) ==
1033 		    POC_INVAL) {
1034 			xmlFree(name);
1035 			xmlXPathFreeObject(path);
1036 			xmlXPathFreeContext(ctx);
1037 			while (i-- >= 0)
1038 				pool_value_free(result[i]);
1039 			free(result);
1040 			return (NULL);
1041 		}
1042 		if (pool_value_set_name(result[i], (const char *)name) !=
1043 		    PO_SUCCESS) {
1044 			xmlFree(name);
1045 			xmlXPathFreeObject(path);
1046 			xmlXPathFreeContext(ctx);
1047 			while (i-- >= 0)
1048 				pool_value_free(result[i]);
1049 			free(result);
1050 			return (NULL);
1051 		}
1052 		xmlFree(name);
1053 	}
1054 	xmlXPathFreeObject(path);
1055 	xmlXPathFreeContext(ctx);
1056 	return (result);
1057 }
1058 
1059 /*
1060  * Store a pointer to one of our data types in the _private member of each
1061  * XML data node contained within the passed node. Note this function is
1062  * recursive and so all sub-nodes are also shadowed. Only shadow the nodes
1063  * which we are interested in, i.e. system, pool, res and comp
1064  */
1065 static int
create_shadow(xmlNodePtr node)1066 create_shadow(xmlNodePtr node)
1067 {
1068 	xmlNodePtr sib;
1069 	int ret = PO_SUCCESS;
1070 	/* Create a data structure of the appropriate type */
1071 
1072 	if (0 == (xmlStrcmp(node->name,
1073 	    BAD_CAST element_class_tags[PEC_SYSTEM]))) {
1074 		ret = pool_xml_elem_wrap(node, PEC_SYSTEM, PREC_INVALID,
1075 		    PCEC_INVALID);
1076 	} else if (0 == (xmlStrcmp(node->name,
1077 	    BAD_CAST element_class_tags[PEC_POOL]))) {
1078 		ret = pool_xml_elem_wrap(node, PEC_POOL, PREC_INVALID,
1079 		    PCEC_INVALID);
1080 	} else if (0 == (xmlStrcmp(node->name,
1081 	    BAD_CAST element_class_tags[PEC_RES_COMP]))) {
1082 		xmlChar *data;
1083 		pool_resource_elem_class_t res_class;
1084 		data = xmlGetProp(node, BAD_CAST c_type);
1085 
1086 		res_class = pool_resource_elem_class_from_string((char *)data);
1087 		xmlFree(data);
1088 		ret = pool_xml_elem_wrap(node, PEC_RES_COMP, res_class,
1089 		    PCEC_INVALID);
1090 	} else if (0 == (xmlStrcmp(node->name,
1091 	    BAD_CAST element_class_tags[PEC_RES_AGG]))) {
1092 		xmlChar *data;
1093 		pool_resource_elem_class_t res_class;
1094 		data = xmlGetProp(node, BAD_CAST c_type);
1095 
1096 		res_class = pool_resource_elem_class_from_string((char *)data);
1097 		xmlFree(data);
1098 		ret = pool_xml_elem_wrap(node, PEC_RES_AGG, res_class,
1099 		    PCEC_INVALID);
1100 	} else if (0 == (xmlStrcmp(node->name,
1101 	    BAD_CAST element_class_tags[PEC_COMP]))) {
1102 		xmlChar *data;
1103 		pool_component_elem_class_t comp_class;
1104 		data = xmlGetProp(node, BAD_CAST c_type);
1105 
1106 		comp_class = pool_component_elem_class_from_string(
1107 		    (char *)data);
1108 		xmlFree(data);
1109 		ret = pool_xml_elem_wrap(node, PEC_COMP, PREC_INVALID,
1110 		    comp_class);
1111 	}
1112 	/* Have to shadow all children and all siblings */
1113 	for (sib = node->children; sib != NULL; sib = sib->next) {
1114 		if ((ret = create_shadow(sib)) != PO_SUCCESS)
1115 			break;
1116 	}
1117 	return (ret);
1118 }
1119 
1120 
1121 /*
1122  * XML Data access and navigation APIs
1123  */
1124 
1125 /*
1126  * Close the configuration. There are a few steps to closing a configuration:
1127  * - Unlock the backing file (if there is one)
1128  * - Close the file (if there is one)
1129  * - Free the shadow memory	}Done in pool_xml_free_doc
1130  * - Free the document		}
1131  * - Free the data provider for this configuration
1132  * - Free the configuration location specifier
1133  * Returns PO_SUCCESS/PO_FAIL
1134  */
1135 static int
pool_xml_close(pool_conf_t * conf)1136 pool_xml_close(pool_conf_t *conf)
1137 {
1138 	pool_xml_connection_t *pxc = (pool_xml_connection_t *)conf->pc_prov;
1139 	int ret = PO_SUCCESS;
1140 
1141 	if (pxc->pxc_file != NULL) {
1142 		/* Close (and implicitly) unlock the file */
1143 		if (fclose(pxc->pxc_file) != 0) {
1144 			pool_seterror(POE_SYSTEM);
1145 			ret = PO_FAIL;
1146 		}
1147 		pxc->pxc_file = NULL;
1148 	}
1149 	/* Close the xml specific parts */
1150 	(void) pool_xml_free_doc(conf);
1151 	pool_xml_connection_free((pool_xml_connection_t *)conf->pc_prov);
1152 	return (ret);
1153 }
1154 
1155 /*
1156  * Remove the configuration from the backing store. In XML terms delete
1157  * the file backing the configuration. You need a copy of the location
1158  * since the pool_conf_close function, frees the location.
1159  * Returns PO_SUCCESS/PO_FAIL
1160  */
1161 static int
pool_xml_remove(pool_conf_t * conf)1162 pool_xml_remove(pool_conf_t *conf)
1163 {
1164 	if (pool_conf_location(conf) != NULL) {
1165 		/* First unlink the file, to prevent races on open */
1166 		if (unlink(pool_conf_location(conf)) != 0) {
1167 			pool_seterror(POE_SYSTEM);
1168 			return (PO_FAIL);
1169 		}
1170 		/* Now close the configuration */
1171 		(void) pool_conf_close(conf);
1172 		return (PO_SUCCESS);
1173 	}
1174 	return (PO_FAIL);
1175 }
1176 
1177 /*
1178  * Validate the configuration. There are three levels of validation, loose,
1179  * strict and runtime. In this, XML, implementation, loose is mapped to XML
1180  * validation, strict implements additional application level validation
1181  * checks, e.g. all pools must have unique names, runtime ensures that this
1182  * configuration would instantiate on the current system.
1183  *
1184  * Returns PO_SUCCESS/PO_FAIL
1185  */
1186 static int
pool_xml_validate(const pool_conf_t * conf,pool_valid_level_t level)1187 pool_xml_validate(const pool_conf_t *conf, pool_valid_level_t level)
1188 {
1189 	pool_xml_connection_t *pxc = (pool_xml_connection_t *)conf->pc_prov;
1190 	xmlValidCtxtPtr cvp;
1191 
1192 	if ((cvp = xmlNewValidCtxt()) == NULL) {
1193 		pool_seterror(POE_INVALID_CONF);
1194 		return (PO_FAIL);
1195 	}
1196 	cvp->error    = pool_error_func;
1197 	cvp->warning  = pool_error_func;
1198 
1199 	if (xmlValidateDocument(cvp, pxc->pxc_doc) == 0) {
1200 		xmlFreeValidCtxt(cvp);
1201 		pool_seterror(POE_INVALID_CONF);
1202 		return (PO_FAIL);
1203 	}
1204 	xmlFreeValidCtxt(cvp);
1205 
1206 	if (level >= POV_RUNTIME) {
1207 		/*
1208 		 * Note: This is resource specific.
1209 		 */
1210 		return (((pool_validate_resource(conf, "pset", c_min_prop, 0) ==
1211 		    PO_SUCCESS) &&
1212 		    (pool_validate_resource(conf, "pset", c_max_prop, 0) ==
1213 		    PO_SUCCESS)) ? PO_SUCCESS : PO_FAIL);
1214 	}
1215 	return (PO_SUCCESS);
1216 }
1217 
1218 /*
1219  * Commit the configuration to the backing store. In XML terms this means
1220  * write the changes to the backing file. Read the comments below for details
1221  * on exactly how this operation is performed.
1222  * Returns PO_SUCCESS/PO_FAIL
1223  */
1224 static int
pool_xml_commit(pool_conf_t * conf)1225 pool_xml_commit(pool_conf_t *conf)
1226 {
1227 	pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov;
1228 	xmlOutputBufferPtr buf;
1229 
1230 	/*
1231 	 * Ensure that the configuration file has no contents
1232 	 */
1233 	if (fseek(prov->pxc_file, 0, SEEK_SET) != 0) {
1234 		pool_seterror(POE_SYSTEM);
1235 		return (PO_FAIL);
1236 	}
1237 
1238 	if (ftruncate(fileno(prov->pxc_file), 0) == -1) {
1239 		pool_seterror(POE_SYSTEM);
1240 		return (PO_FAIL);
1241 	}
1242 	/*
1243 	 * Create an XML output buffer and write out the contents of the
1244 	 * configuration to the file.
1245 	 */
1246 	if ((buf = xmlOutputBufferCreateFile(prov->pxc_file, NULL)) == NULL) {
1247 		pool_seterror(POE_DATASTORE);
1248 		return (PO_FAIL);
1249 	}
1250 
1251 	if (xmlSaveFormatFileTo(buf, prov->pxc_doc, NULL, 1) == -1) {
1252 		pool_seterror(POE_DATASTORE);
1253 		return (PO_FAIL);
1254 	}
1255 
1256 	return (PO_SUCCESS);
1257 }
1258 
1259 /*
1260  * Export the configuration in the specified format to the specified location.
1261  * The only format implemented now is the native format, which saves the
1262  * active configuration to the supplied location.
1263  * Returns PO_SUCCESS/PO_FAIL
1264  */
1265 static int
pool_xml_export(const pool_conf_t * conf,const char * location,pool_export_format_t fmt)1266 pool_xml_export(const pool_conf_t *conf, const char *location,
1267     pool_export_format_t fmt)
1268 {
1269 	int ret;
1270 
1271 	switch (fmt) {
1272 	case POX_NATIVE:
1273 		ret = xmlSaveFormatFile(location,
1274 		    ((pool_xml_connection_t *)conf->pc_prov)->pxc_doc,
1275 		    1);
1276 		if (ret == -1) {
1277 			pool_seterror(POE_SYSTEM);
1278 			return (PO_FAIL);
1279 		} else
1280 			return (PO_SUCCESS);
1281 
1282 	default:
1283 		pool_seterror(POE_BADPARAM);
1284 		return (PO_FAIL);
1285 	}
1286 }
1287 
1288 /*
1289  * Discard the configuration and restore the configuration to the values
1290  * specified in the configuration location.
1291  * Returns PO_SUCCESS/PO_FAIL
1292  */
1293 static int
pool_xml_rollback(pool_conf_t * conf)1294 pool_xml_rollback(pool_conf_t *conf)
1295 {
1296 	pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov;
1297 
1298 	/* Rollback the file pointer ready for the reparse */
1299 	if (fseek(prov->pxc_file, 0, SEEK_SET) != 0) {
1300 		pool_seterror(POE_SYSTEM);
1301 		return (PO_FAIL);
1302 	}
1303 	/* Reparse the document */
1304 	/* In XML terms this means, discard and reparse the document */
1305 	(void) pool_xml_free_doc(conf);
1306 	if (pool_xml_parse_document(conf) == PO_FAIL)
1307 		return (PO_FAIL);
1308 	return (PO_SUCCESS);
1309 }
1310 
1311 /*
1312  * Allocate a new pool_elem_t in the supplied configuration of the specified
1313  * class.
1314  * Returns element pointer/NULL
1315  */
1316 static void
pool_xml_elem_init(pool_conf_t * conf,pool_xml_elem_t * elem,pool_elem_class_t class,pool_resource_elem_class_t res_class,pool_component_elem_class_t comp_class)1317 pool_xml_elem_init(pool_conf_t *conf, pool_xml_elem_t *elem,
1318     pool_elem_class_t class, pool_resource_elem_class_t res_class,
1319     pool_component_elem_class_t comp_class)
1320 {
1321 	pool_elem_t *pe = TO_ELEM(elem);
1322 	pe->pe_conf = conf;
1323 	pe->pe_class = class;
1324 	pe->pe_resource_class = res_class;
1325 	pe->pe_component_class = comp_class;
1326 	/* Set up the function pointers for element manipulation */
1327 	pe->pe_get_prop = pool_xml_get_property;
1328 	pe->pe_put_prop = pool_xml_put_property;
1329 	pe->pe_rm_prop = pool_xml_rm_property;
1330 	pe->pe_get_props = pool_xml_get_properties;
1331 	pe->pe_remove = pool_xml_elem_remove;
1332 	pe->pe_get_container = pool_xml_get_container;
1333 	pe->pe_set_container = pool_xml_set_container;
1334 	/*
1335 	 * Specific initialisation for different types of element
1336 	 */
1337 	if (class == PEC_POOL) {
1338 		pool_xml_pool_t *pp = (pool_xml_pool_t *)elem;
1339 		pp->pp_associate = pool_xml_pool_associate;
1340 		pp->pp_dissociate = pool_xml_pool_dissociate;
1341 	}
1342 	if (class == PEC_RES_COMP || class == PEC_RES_AGG) {
1343 		pool_xml_resource_t *pr = (pool_xml_resource_t *)elem;
1344 		pr->pr_is_system = pool_xml_resource_is_system;
1345 		pr->pr_can_associate = pool_xml_resource_can_associate;
1346 	}
1347 }
1348 
1349 /*
1350  * "Wrap" a suplied XML node with a pool_elem_t sub-type of the supplied
1351  * class.
1352  * Returns PO_SUCCESS/PO_FAIL
1353  */
1354 static int
pool_xml_elem_wrap(xmlNodePtr node,pool_elem_class_t class,pool_resource_elem_class_t res_class,pool_component_elem_class_t comp_class)1355 pool_xml_elem_wrap(xmlNodePtr node, pool_elem_class_t class,
1356     pool_resource_elem_class_t res_class,
1357     pool_component_elem_class_t comp_class)
1358 {
1359 	pool_conf_t *conf = node->doc->_private;
1360 	pool_xml_elem_t *elem;
1361 	/* Need to do some messing about to support SubTypes */
1362 	switch (class) {
1363 	case PEC_SYSTEM:
1364 		if ((elem = malloc(sizeof (pool_xml_system_t))) == NULL) {
1365 			pool_seterror(POE_SYSTEM);
1366 			return (PO_FAIL);
1367 		}
1368 		(void) memset(elem, 0, sizeof (pool_xml_system_t));
1369 		break;
1370 	case PEC_POOL:
1371 		if ((elem = malloc(sizeof (pool_xml_pool_t))) == NULL) {
1372 			pool_seterror(POE_SYSTEM);
1373 			return (PO_FAIL);
1374 		}
1375 		(void) memset(elem, 0, sizeof (pool_xml_pool_t));
1376 		break;
1377 	case PEC_RES_COMP:
1378 	case PEC_RES_AGG:
1379 		if ((elem = malloc(sizeof (pool_xml_resource_t))) == NULL) {
1380 			pool_seterror(POE_SYSTEM);
1381 			return (PO_FAIL);
1382 		}
1383 		(void) memset(elem, 0, sizeof (pool_xml_resource_t));
1384 		break;
1385 	case PEC_COMP:
1386 		if ((elem = malloc(sizeof (pool_xml_component_t))) == NULL) {
1387 			pool_seterror(POE_SYSTEM);
1388 			return (PO_FAIL);
1389 		}
1390 		(void) memset(elem, 0, sizeof (pool_xml_component_t));
1391 		break;
1392 	}
1393 	pool_xml_elem_init(conf, elem, class, res_class, comp_class);
1394 	node->_private = elem;
1395 	elem->pxe_node = node;
1396 	return (PO_SUCCESS);
1397 }
1398 
1399 /*
1400  * Associate a pool to the default resource for the supplied resource
1401  * type.
1402  */
1403 int
pool_assoc_default_resource_type(pool_t * pool,pool_resource_elem_class_t type)1404 pool_assoc_default_resource_type(pool_t *pool, pool_resource_elem_class_t type)
1405 {
1406 	pool_value_t *props[] = { NULL, NULL, NULL };
1407 	uint_t rl_size;
1408 	pool_resource_t **rsl;
1409 	pool_conf_t *conf = TO_ELEM(pool)->pe_conf;
1410 	char_buf_t *cb = NULL;
1411 	pool_value_t val0 = POOL_VALUE_INITIALIZER;
1412 	pool_value_t val1 = POOL_VALUE_INITIALIZER;
1413 
1414 	props[0] = &val0;
1415 	props[1] = &val1;
1416 
1417 
1418 	if (pool_value_set_string(props[0], pool_resource_type_string(type)) !=
1419 	    PO_SUCCESS ||
1420 	    pool_value_set_name(props[0], c_type) != PO_SUCCESS) {
1421 		return (PO_FAIL);
1422 	}
1423 
1424 	if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL) {
1425 		return (PO_FAIL);
1426 	}
1427 
1428 	if (set_char_buf(cb, "%s.default",
1429 	    pool_resource_type_string(type)) !=
1430 	    PO_SUCCESS) {
1431 		free_char_buf(cb);
1432 		return (PO_FAIL);
1433 	}
1434 	if (pool_value_set_name(props[1], cb->cb_buf) != PO_SUCCESS) {
1435 		free_char_buf(cb);
1436 		return (PO_FAIL);
1437 	}
1438 	pool_value_set_bool(props[1], PO_TRUE);
1439 	free_char_buf(cb);
1440 
1441 	if ((rsl = pool_query_resources(conf, &rl_size, props)) == NULL) {
1442 		pool_seterror(POE_INVALID_CONF);
1443 		return (PO_FAIL);
1444 	}
1445 
1446 	/*
1447 	 * One default resource set per type
1448 	 */
1449 	if (rl_size != 1) {
1450 		free(rsl);
1451 		pool_seterror(POE_INVALID_CONF);
1452 		return (PO_FAIL);
1453 	}
1454 	if (pool_associate(conf, pool, rsl[0])  < 0) {
1455 		free(rsl);
1456 		pool_seterror(POE_INVALID_CONF);
1457 		return (PO_FAIL);
1458 	}
1459 	free(rsl);
1460 	return (PO_SUCCESS);
1461 }
1462 
1463 /*
1464  * Create an XML node in the supplied configuration with a pool_elem_t
1465  * sub-type of the supplied class.
1466  * Returns pool_elem_t pointer/NULL
1467  */
1468 static pool_elem_t *
pool_xml_elem_create(pool_conf_t * conf,pool_elem_class_t class,pool_resource_elem_class_t res_class,pool_component_elem_class_t comp_class)1469 pool_xml_elem_create(pool_conf_t *conf, pool_elem_class_t class,
1470     pool_resource_elem_class_t res_class,
1471     pool_component_elem_class_t comp_class)
1472 {
1473 	/* In XML terms, create an element of the appropriate class */
1474 	pool_xml_elem_t *elem;
1475 	pool_elem_t *parent;
1476 	pool_system_t *parent_system;
1477 
1478 	if (class == PEC_INVALID) {
1479 		pool_seterror(POE_BADPARAM);
1480 		return (NULL);
1481 	}
1482 
1483 	/* Now create the XML component and add to it's parent */
1484 	/*
1485 	 * If we know the class of an element, we know it's parent.
1486 	 * PEC_POOL, the parent must be the system node
1487 	 * PEC_RES, treat as pool.
1488 	 * PEC_COMP, we don't know the parent, leave this up to the
1489 	 * create_comp function.
1490 	 */
1491 	/* Since we know the subtype we can create and populate the sub-type */
1492 	switch (class) {
1493 	case PEC_POOL:
1494 		if ((parent_system = pool_conf_system(conf)) == NULL) {
1495 			pool_seterror(POE_INVALID_CONF);
1496 			return (NULL);
1497 		}
1498 		if ((parent = pool_system_elem(parent_system)) == NULL) {
1499 			pool_seterror(POE_INVALID_CONF);
1500 			return (NULL);
1501 		}
1502 		if ((elem = malloc(sizeof (pool_xml_system_t))) == NULL) {
1503 			pool_seterror(POE_SYSTEM);
1504 			return (NULL);
1505 		}
1506 		(void) memset(elem, 0, sizeof (pool_xml_system_t));
1507 		if ((elem->pxe_node = node_create_with_id(
1508 		    ((pool_xml_elem_t *)parent)->pxe_node,
1509 		    BAD_CAST element_class_tags[class])) == NULL) {
1510 			pool_seterror(POE_DATASTORE);
1511 			(void) pool_xml_elem_remove((pool_elem_t *)elem);
1512 			return (NULL);
1513 		}
1514 		break;
1515 	case PEC_RES_COMP:
1516 	case PEC_RES_AGG:
1517 		if ((parent_system = pool_conf_system(conf)) == NULL) {
1518 			pool_seterror(POE_INVALID_CONF);
1519 			return (NULL);
1520 		}
1521 		if ((parent = pool_system_elem(parent_system)) == NULL) {
1522 			pool_seterror(POE_INVALID_CONF);
1523 			return (NULL);
1524 		}
1525 		if ((elem = malloc(sizeof (pool_xml_resource_t))) == NULL) {
1526 			pool_seterror(POE_SYSTEM);
1527 			return (NULL);
1528 		}
1529 		(void) memset(elem, 0, sizeof (pool_xml_resource_t));
1530 		if ((elem->pxe_node = node_create_with_id
1531 		    (((pool_xml_elem_t *)parent)->pxe_node,
1532 		    BAD_CAST element_class_tags[class])) == NULL) {
1533 			pool_seterror(POE_DATASTORE);
1534 			(void) pool_xml_elem_remove((pool_elem_t *)elem);
1535 			return (NULL);
1536 		}
1537 		break;
1538 	case PEC_COMP:
1539 		if ((elem = malloc(sizeof (pool_xml_component_t))) == NULL) {
1540 			pool_seterror(POE_SYSTEM);
1541 			return (NULL);
1542 		}
1543 		(void) memset(elem, 0, sizeof (pool_xml_component_t));
1544 		if ((elem->pxe_node = node_create(NULL,
1545 		    BAD_CAST element_class_tags[class])) == NULL) {
1546 			pool_seterror(POE_DATASTORE);
1547 			(void) pool_xml_elem_remove((pool_elem_t *)elem);
1548 			return (NULL);
1549 		}
1550 		break;
1551 	default:
1552 		pool_seterror(POE_BADPARAM);
1553 		return (NULL);
1554 	}
1555 	pool_xml_elem_init(conf, elem, class, res_class, comp_class);
1556 	elem->pxe_node->_private = elem;
1557 	if (class == PEC_RES_COMP || class == PEC_RES_AGG ||
1558 	    class == PEC_COMP) {
1559 		/*
1560 		 * Put the type and an invalid sys_id on the node.
1561 		 */
1562 		if (xmlSetProp(elem->pxe_node, BAD_CAST c_sys_prop,
1563 		    BAD_CAST POOL_SYSID_BAD_STRING) == NULL) {
1564 			pool_seterror(POE_DATASTORE);
1565 			(void) pool_xml_elem_remove((pool_elem_t *)elem);
1566 			return (NULL);
1567 		}
1568 		if (xmlSetProp(elem->pxe_node, BAD_CAST c_type,
1569 		    BAD_CAST pool_elem_class_string(
1570 		    (pool_elem_t *)elem)) == NULL) {
1571 			pool_seterror(POE_DATASTORE);
1572 			(void) pool_xml_elem_remove((pool_elem_t *)elem);
1573 			return (NULL);
1574 		}
1575 	}
1576 	if (class == PEC_POOL) {
1577 		/*
1578 		 * Note: This is resource specific.
1579 		 */
1580 		if (pool_assoc_default_resource_type(pool_elem_pool(
1581 		    (pool_elem_t *)elem), PREC_PSET) == PO_FAIL) {
1582 			(void) pool_xml_elem_remove((pool_elem_t *)elem);
1583 			return (NULL);
1584 		}
1585 	}
1586 	return ((pool_elem_t *)elem);
1587 }
1588 
1589 /*
1590  * Allocate a data provider for the supplied configuration and optionally
1591  * discover resources.
1592  * The data provider is the cross over point from the "abstract" configuration
1593  * functions into the data representation specific manipulation routines.
1594  * This function sets up all the required pointers to create an XML aware
1595  * data provider.
1596  * Returns PO_SUCCESS/PO_FAIL
1597  */
1598 int
pool_xml_connection_alloc(pool_conf_t * conf,int oflags)1599 pool_xml_connection_alloc(pool_conf_t *conf, int oflags)
1600 {
1601 	pool_xml_connection_t *prov;
1602 
1603 	xml_init();
1604 	if ((prov = malloc(sizeof (pool_xml_connection_t))) == NULL) {
1605 		pool_seterror(POE_SYSTEM);
1606 		return (PO_FAIL);
1607 	}
1608 	(void) memset(prov, 0, sizeof (pool_xml_connection_t));
1609 	/*
1610 	 * Initialise data members
1611 	 */
1612 	prov->pc_name = strdup("LIBXML 2.4.0");
1613 	prov->pc_store_type = XML_DATA_STORE;
1614 	prov->pc_oflags = oflags;
1615 	/*
1616 	 * Initialise function pointers
1617 	 */
1618 	prov->pc_close = pool_xml_close;
1619 	prov->pc_validate = pool_xml_validate;
1620 	prov->pc_commit = pool_xml_commit;
1621 	prov->pc_export = pool_xml_export;
1622 	prov->pc_rollback = pool_xml_rollback;
1623 	prov->pc_exec_query = pool_xml_exec_query;
1624 	prov->pc_elem_create = pool_xml_elem_create;
1625 	prov->pc_remove = pool_xml_remove;
1626 	prov->pc_res_xfer = pool_xml_res_transfer;
1627 	prov->pc_res_xxfer = pool_xml_res_xtransfer;
1628 	/*
1629 	 * End of common initialisation
1630 	 */
1631 	/*
1632 	 * Associate the provider to it's configuration
1633 	 */
1634 	conf->pc_prov = (pool_connection_t *)prov;
1635 	/*
1636 	 * At this point the configuration provider has been initialized,
1637 	 * mark the configuration as valid so that the various routines
1638 	 * which rely on a valid configuration will work correctly.
1639 	 */
1640 	conf->pc_state = POF_VALID;
1641 
1642 	if ((oflags & PO_CREAT) != 0) {
1643 		pool_conf_t *dyn;
1644 
1645 		if ((dyn = pool_conf_alloc()) == NULL)
1646 			return (PO_FAIL);
1647 
1648 		if (pool_conf_open(dyn, pool_dynamic_location(),
1649 		    PO_RDONLY) != PO_SUCCESS) {
1650 			pool_conf_free(dyn);
1651 			return (PO_FAIL);
1652 		}
1653 
1654 		if (pool_conf_export(dyn, conf->pc_location,
1655 		    POX_NATIVE) != PO_SUCCESS) {
1656 			(void) pool_conf_close(dyn);
1657 			pool_conf_free(dyn);
1658 			return (PO_FAIL);
1659 		}
1660 		(void) pool_conf_close(dyn);
1661 		pool_conf_free(dyn);
1662 	}
1663 
1664 	if (pool_xml_open_file(conf) == PO_FAIL) {
1665 		(void) pool_xml_close(conf);
1666 		return (PO_FAIL);
1667 	}
1668 
1669 	return (PO_SUCCESS);
1670 }
1671 
1672 /*
1673  * Free the resources for an XML data provider.
1674  */
1675 static void
pool_xml_connection_free(pool_xml_connection_t * prov)1676 pool_xml_connection_free(pool_xml_connection_t *prov)
1677 {
1678 	free((void *)prov->pc_name);
1679 	free(prov);
1680 }
1681 
1682 /*
1683  * Allocate a result set. The Result Set stores the result of an XPath
1684  * query along with the parameters used to create the result set (for
1685  * debugging purposes).
1686  * Returns pool_xml_result_set_t pointer/NULL
1687  */
1688 static pool_xml_result_set_t *
pool_xml_result_set_alloc(const pool_conf_t * conf)1689 pool_xml_result_set_alloc(const pool_conf_t *conf)
1690 {
1691 	pool_xml_result_set_t *rs;
1692 
1693 	if ((rs = malloc(sizeof (pool_xml_result_set_t))) == NULL) {
1694 		pool_seterror(POE_SYSTEM);
1695 		return (NULL);
1696 	}
1697 	(void) memset(rs, 0, sizeof (pool_xml_result_set_t));
1698 	rs->prs_conf = conf;
1699 	rs->prs_index = -1;
1700 	rs->prs_active = PO_TRUE;
1701 	/* Fix up the result set accessor functions to the xml specfic ones */
1702 	rs->prs_next = pool_xml_rs_next;
1703 	rs->prs_prev = pool_xml_rs_prev;
1704 	rs->prs_first = pool_xml_rs_first;
1705 	rs->prs_last = pool_xml_rs_last;
1706 	rs->prs_get_index = pool_xml_rs_get_index;
1707 	rs->prs_set_index = pool_xml_rs_set_index;
1708 	rs->prs_close = pool_xml_rs_close;
1709 	rs->prs_count = pool_xml_rs_count;
1710 	return (rs);
1711 }
1712 
1713 /*
1714  * Free a result set. Ensure that the resources are all released at this point.
1715  */
1716 static void
pool_xml_result_set_free(pool_xml_result_set_t * rs)1717 pool_xml_result_set_free(pool_xml_result_set_t *rs)
1718 {
1719 	if (rs->pxr_path != NULL)
1720 		xmlXPathFreeObject(rs->pxr_path);
1721 	if (rs->pxr_ctx != NULL)
1722 		xmlXPathFreeContext(rs->pxr_ctx);
1723 	free(rs);
1724 }
1725 
1726 /*
1727  * Transfer size from one resource to another.
1728  * Returns PO_SUCCESS/PO_FAIL
1729  */
1730 /* ARGSUSED */
1731 int
pool_xml_res_transfer(pool_resource_t * src,pool_resource_t * tgt,uint64_t size)1732 pool_xml_res_transfer(pool_resource_t *src, pool_resource_t *tgt, uint64_t size)
1733 {
1734 	return (PO_SUCCESS);
1735 }
1736 
1737 /*
1738  * Transfer components rl from one resource to another.
1739  * Returns PO_SUCCESS/PO_FAIL
1740  */
1741 /* ARGSUSED */
1742 int
pool_xml_res_xtransfer(pool_resource_t * src,pool_resource_t * tgt,pool_component_t ** rl)1743 pool_xml_res_xtransfer(pool_resource_t *src, pool_resource_t *tgt,
1744     pool_component_t **rl) {
1745 	int i;
1746 
1747 	/*
1748 	 * Walk the Result Set and move the resource components
1749 	 */
1750 	for (i = 0; rl[i] != NULL; i++) {
1751 		if (pool_set_container(TO_ELEM(tgt), TO_ELEM(rl[i])) ==
1752 		    PO_FAIL) {
1753 			return (PO_FAIL);
1754 		}
1755 	}
1756 	return (PO_SUCCESS);
1757 }
1758 
1759 /*
1760  * Return the next element in a result set.
1761  * Returns pool_elem_t pointer/NULL
1762  */
1763 static pool_elem_t *
pool_xml_rs_next(pool_result_set_t * set)1764 pool_xml_rs_next(pool_result_set_t *set)
1765 {
1766 	pool_elem_t *next;
1767 	/* Since I know this is an XML result set */
1768 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1769 
1770 	/* Update the context node */
1771 	if (xset->prs_index == xset->pxr_path->nodesetval->nodeNr - 1)
1772 		return (NULL);
1773 	next =
1774 	    xset->pxr_path->nodesetval->nodeTab[++xset->prs_index]->_private;
1775 	return (next);
1776 }
1777 
1778 /*
1779  * Return the previous element in a result set.
1780  * Returns pool_elem_t pointer/NULL
1781  */
1782 static pool_elem_t *
pool_xml_rs_prev(pool_result_set_t * set)1783 pool_xml_rs_prev(pool_result_set_t *set)
1784 {
1785 	pool_elem_t *prev;
1786 	/* Since I know this is an XML result set */
1787 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1788 
1789 	/* Update the context node */
1790 	if (xset->prs_index < 0)
1791 		return (NULL);
1792 	prev =
1793 	    xset->pxr_path->nodesetval->nodeTab[xset->prs_index--]->_private;
1794 	return (prev);
1795 }
1796 
1797 /*
1798  * Sets the current index in a result set.
1799  * Returns PO_SUCCESS/PO_FAIL
1800  */
1801 static int
pool_xml_rs_set_index(pool_result_set_t * set,int index)1802 pool_xml_rs_set_index(pool_result_set_t *set, int index)
1803 {
1804 	/* Since I know this is an XML result set */
1805 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1806 
1807 	if (index < 0 || index >= xset->pxr_path->nodesetval->nodeNr) {
1808 		pool_seterror(POE_BADPARAM);
1809 		return (PO_FAIL);
1810 	}
1811 	xset->prs_index = index;
1812 	return (PO_SUCCESS);
1813 }
1814 
1815 /*
1816  * Return the current index in a result set.
1817  * Returns current index
1818  */
1819 static int
pool_xml_rs_get_index(pool_result_set_t * set)1820 pool_xml_rs_get_index(pool_result_set_t *set)
1821 {
1822 	/* Since I know this is an XML result set */
1823 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1824 
1825 	return (xset->prs_index);
1826 }
1827 
1828 /*
1829  * Return the first element in a result set.
1830  * Returns pool_elem_t pointer/NULL
1831  */
1832 static pool_elem_t *
pool_xml_rs_first(pool_result_set_t * set)1833 pool_xml_rs_first(pool_result_set_t *set)
1834 {
1835 	/* Since I know this is an XML result set */
1836 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1837 
1838 	/* Update the context node */
1839 	return (xset->pxr_path->nodesetval->nodeTab[0]->_private);
1840 }
1841 
1842 /*
1843  * Return the last element in a result set.
1844  * Returns pool_elem_t pointer/NULL
1845  */
1846 static pool_elem_t *
pool_xml_rs_last(pool_result_set_t * set)1847 pool_xml_rs_last(pool_result_set_t *set)
1848 {
1849 	/* Since I know this is an XML result set */
1850 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1851 
1852 	/* Update the context node */
1853 	return (xset->pxr_path->nodesetval->
1854 	    nodeTab[xset->pxr_path->nodesetval->nodeNr-1]->_private);
1855 }
1856 
1857 /*
1858  * Return the number of results in a result set.
1859  * Returns result count
1860  */
1861 static int
pool_xml_rs_count(pool_result_set_t * set)1862 pool_xml_rs_count(pool_result_set_t *set)
1863 {
1864 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1865 
1866 	return (xset->pxr_path->nodesetval->nodeNr);
1867 }
1868 
1869 
1870 /*
1871  * Close a result set. Remove this result set from the list of results and
1872  * free the resources
1873  * Returns PO_SUCCESS/PO_FAIL
1874  */
1875 static int
pool_xml_rs_close(pool_result_set_t * set)1876 pool_xml_rs_close(pool_result_set_t *set)
1877 {
1878 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1879 
1880 	pool_xml_result_set_free(xset);
1881 	return (PO_SUCCESS);
1882 }
1883 
1884 /*
1885  * Set the container for a node.
1886  * Returns PO_SUCCESS/PO_FAIL
1887  */
1888 static int
pool_xml_set_container(pool_elem_t * pp,pool_elem_t * pc)1889 pool_xml_set_container(pool_elem_t *pp, pool_elem_t *pc)
1890 {
1891 	pool_xml_elem_t *pxp;
1892 	pool_xml_elem_t *pxc;
1893 	xmlNodePtr parent;
1894 
1895 	pxp = (pool_xml_elem_t *)pp;
1896 	pxc = (pool_xml_elem_t *)pc;
1897 	parent = pxc->pxe_node->parent;
1898 
1899 	xmlUnlinkNode(pxc->pxe_node);
1900 	if (xmlAddChild(pxp->pxe_node, pxc->pxe_node) == NULL) {
1901 		/* Try to move back */
1902 		(void) xmlAddChild(parent, pxc->pxe_node);
1903 		pool_seterror(POE_INVALID_CONF);
1904 		return (PO_FAIL);
1905 	}
1906 	pc->pe_conf = pp->pe_conf;
1907 	return (PO_SUCCESS);
1908 }
1909 /*
1910  * Get the container for a node.
1911  * Returns Container/NULL
1912  */
1913 static pool_elem_t *
pool_xml_get_container(const pool_elem_t * pc)1914 pool_xml_get_container(const pool_elem_t *pc)
1915 {
1916 	pool_xml_elem_t *pxc = (pool_xml_elem_t *)pc;
1917 
1918 	return ((pool_elem_t *)pxc->pxe_node->parent->_private);
1919 }
1920 
1921 /*
1922  * Note: This function is resource specific, needs extending for other
1923  * resource types.
1924  */
1925 int
pool_xml_resource_is_system(const pool_resource_t * pr)1926 pool_xml_resource_is_system(const pool_resource_t *pr)
1927 {
1928 	switch (pool_resource_elem_class(TO_ELEM(pr))) {
1929 	case PREC_PSET:
1930 		return (PSID_IS_SYSSET(
1931 		    elem_get_sysid(TO_ELEM(pr))));
1932 	default:
1933 		return (PO_FALSE);
1934 	}
1935 }
1936 
1937 /*
1938  * Note: This function is resource specific, needs extending for other
1939  * resource types.
1940  */
1941 int
pool_xml_resource_can_associate(const pool_resource_t * pr)1942 pool_xml_resource_can_associate(const pool_resource_t *pr)
1943 {
1944 	switch (pool_resource_elem_class(TO_ELEM(pr))) {
1945 	case PREC_PSET:
1946 		return (PO_TRUE);
1947 	default:
1948 		return (PO_FALSE);
1949 	}
1950 }
1951 
1952 /*
1953  * Note: This function is resource specific. It must be extended to support
1954  * multiple resource types.
1955  */
1956 int
pool_xml_pool_associate(pool_t * pool,const pool_resource_t * pr)1957 pool_xml_pool_associate(pool_t *pool, const pool_resource_t *pr)
1958 {
1959 	pool_value_t val = POOL_VALUE_INITIALIZER;
1960 
1961 	if (pool_xml_get_property(TO_ELEM(pr),
1962 	    "pset.ref_id", &val) != POC_STRING)
1963 		return (PO_FAIL);
1964 	if (pool_xml_put_property(TO_ELEM(pool), "pool.res", &val) !=
1965 	    PO_SUCCESS)
1966 		return (PO_FAIL);
1967 	return (PO_SUCCESS);
1968 }
1969 
1970 /*
1971  * pool_xml_pool_dissociate() simply finds the default resource for
1972  * the type of resource being dissociated and then calls
1973  * pool_xml_pool_associate() to associate to the default resource.
1974  */
1975 int
pool_xml_pool_dissociate(pool_t * pool,const pool_resource_t * pr)1976 pool_xml_pool_dissociate(pool_t *pool, const pool_resource_t *pr)
1977 {
1978 	const pool_resource_t *default_res;
1979 
1980 	if ((default_res = get_default_resource(pr)) == NULL)
1981 		return (PO_FAIL);
1982 	if (default_res == pr)
1983 		return (PO_SUCCESS);
1984 	return (pool_xml_pool_associate(pool, default_res));
1985 }
1986 
1987 /*
1988  * pool_xml_open_file() opens a file for a configuration. This establishes
1989  * the locks required to ensure data integrity when manipulating a
1990  * configuration.
1991  * Returns PO_SUCCESS/PO_FAIL
1992  */
1993 static int
pool_xml_open_file(pool_conf_t * conf)1994 pool_xml_open_file(pool_conf_t *conf)
1995 {
1996 	struct flock lock;
1997 	struct stat s;
1998 
1999 	pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov;
2000 
2001 	/*
2002 	 * Always close the pxc_file in case there was a previously failed open
2003 	 */
2004 	if (prov->pxc_file != NULL) {
2005 		(void) fclose(prov->pxc_file);
2006 		prov->pxc_file = NULL;
2007 	}
2008 
2009 	/*
2010 	 * Check that the DTD required for this operation is present.
2011 	 * If it isn't fail
2012 	 */
2013 	if (dtd_exists(dtd_location) == PO_FALSE) {
2014 		pool_seterror(POE_DATASTORE);
2015 		return (PO_FAIL);
2016 	}
2017 
2018 	if ((prov->pc_oflags & PO_RDWR) != 0)
2019 		prov->pxc_file = fopen(conf->pc_location, "r+F");
2020 	else /* Assume opening PO_RDONLY */
2021 		prov->pxc_file = fopen(conf->pc_location, "rF");
2022 
2023 	if (prov->pxc_file == NULL) {
2024 		pool_seterror(POE_SYSTEM);
2025 		return (PO_FAIL);
2026 	}
2027 
2028 	/*
2029 	 * Setup the lock for the file
2030 	 */
2031 	lock.l_type = (prov->pc_oflags & PO_RDWR) ? F_WRLCK : F_RDLCK;
2032 	lock.l_whence = SEEK_SET;
2033 	lock.l_start = 0;
2034 	lock.l_len = 0;
2035 	if (fcntl(fileno(prov->pxc_file), F_SETLKW, &lock) == -1) {
2036 		pool_seterror(POE_SYSTEM);
2037 		return (PO_FAIL);
2038 	}
2039 	/*
2040 	 * Check to see if the document was removed whilst waiting for
2041 	 * the lock. If it was return an error.
2042 	 */
2043 	if (stat(conf->pc_location, &s) == -1) {
2044 		(void) fclose(prov->pxc_file);
2045 		prov->pxc_file = NULL;
2046 		pool_seterror(POE_SYSTEM);
2047 		return (PO_FAIL);
2048 	}
2049 	/* Parse the document */
2050 	if (pool_xml_parse_document(conf) != PO_SUCCESS)
2051 		return (PO_FAIL);
2052 	return (PO_SUCCESS);
2053 }
2054 
2055 /*
2056  * Try to work out if an element contains an attribute of the supplied name.
2057  * Search the internal subset first and then the external subset.
2058  * Return PO_TRUE if there is an attribute of that name declared for that
2059  * element.
2060  */
2061 int
pool_is_xml_attr(xmlDocPtr doc,const char * elem,const char * attr)2062 pool_is_xml_attr(xmlDocPtr doc, const char *elem, const char *attr)
2063 {
2064 	xmlDtdPtr internal = xmlGetIntSubset(doc);
2065 	xmlDtdPtr external = doc->extSubset;
2066 
2067 	if (xmlGetDtdAttrDesc(internal, BAD_CAST elem, BAD_CAST attr) == NULL)
2068 		if (xmlGetDtdAttrDesc(external,
2069 		    BAD_CAST elem, BAD_CAST attr) == NULL)
2070 			return (PO_FALSE);
2071 	return (PO_TRUE);
2072 }
2073 
2074 /*
2075  * Execute the specified query using XPath. This complex function relies on
2076  * a couple of helpers to build up an XPath query, pool_build_xpath_buf in
2077  * particular.
2078  * conf - the pool configuration being manipulated
2079  * src - the root of the search, if NULL that means whole document
2080  * src_attr - if supplied means an IDREF(S) search on this attribute
2081  * classes - target classes
2082  * props - target properties
2083  * Returns pool_result_set_t pointer/NULL
2084  */
2085 pool_result_set_t *
pool_xml_exec_query(const pool_conf_t * conf,const pool_elem_t * src,const char * src_attr,pool_elem_class_t classes,pool_value_t ** props)2086 pool_xml_exec_query(const pool_conf_t *conf, const pool_elem_t *src,
2087     const char *src_attr, pool_elem_class_t classes, pool_value_t **props)
2088 {
2089 	char *buf = NULL;
2090 	char_buf_t *cb = NULL;
2091 	pool_xml_result_set_t *rs;
2092 	pool_xml_elem_t *pxe = (pool_xml_elem_t *)src;
2093 	pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov;
2094 
2095 	if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL)
2096 		return (NULL);
2097 
2098 	/*
2099 	 * Prior to building up the complex XPath query, check to see if
2100 	 * src_attr is an IDREF(S). If it is use the IDREF(S) information
2101 	 * to generate the query rather than the other data
2102 	 */
2103 	if (src_attr != NULL) {
2104 		char *tok;
2105 		char *lasts;
2106 		char *or = "";
2107 		xmlChar *id;
2108 
2109 		/*
2110 		 * Check the arguments for consistency
2111 		 */
2112 		if (pool_is_xml_attr(prov->pxc_doc,
2113 		    element_class_tags[src->pe_class], src_attr) != PO_TRUE) {
2114 			free_char_buf(cb);
2115 			pool_seterror(POE_BADPARAM);
2116 			return (NULL);
2117 		}
2118 
2119 		if ((id = xmlGetProp(pxe->pxe_node, BAD_CAST src_attr))
2120 		    == NULL) {
2121 			free_char_buf(cb);
2122 			pool_seterror(POE_DATASTORE);
2123 			return (NULL);
2124 		}
2125 		for (tok = strtok_r((char *)id, "	 ", &lasts);
2126 		    tok != NULL; tok = strtok_r(NULL, "	 ", &lasts)) {
2127 			(void) append_char_buf(cb, "%s//*[@ref_id=\"%s\"]",
2128 			    or, tok);
2129 			or = " | ";
2130 			if ((classes & PEC_QRY_SYSTEM) != 0) {
2131 				if (pool_build_xpath_buf(prov, src, PEC_SYSTEM,
2132 				    props, cb, PO_TRUE) == PO_FAIL) {
2133 					free_char_buf(cb);
2134 					return (NULL);
2135 				}
2136 			}
2137 			if ((classes & PEC_QRY_POOL) != 0) {
2138 				if (pool_build_xpath_buf(prov, src, PEC_POOL,
2139 				    props, cb, PO_TRUE) == PO_FAIL) {
2140 					free_char_buf(cb);
2141 					return (NULL);
2142 				}
2143 			}
2144 			if ((classes & PEC_QRY_RES_COMP) != 0) {
2145 				if (pool_build_xpath_buf(prov, src,
2146 				    PEC_RES_COMP, props, cb, PO_TRUE)
2147 				    == PO_FAIL) {
2148 					free_char_buf(cb);
2149 					return (NULL);
2150 				}
2151 			} else if ((classes & PEC_QRY_RES_AGG) != 0) {
2152 				if (pool_build_xpath_buf(prov, src,
2153 				    PEC_RES_AGG, props, cb, PO_TRUE)
2154 				    == PO_FAIL) {
2155 					free_char_buf(cb);
2156 					return (NULL);
2157 				}
2158 			}
2159 		}
2160 		xmlFree(id);
2161 	} else {
2162 		/*
2163 		 * Build up an XPath query using the supplied parameters.
2164 		 * The basic logic is to:
2165 		 * - Identify which classes are the targets of the query
2166 		 * - For each class work out if the props are attributes or not
2167 		 * - Build up a piece of XPath for each class
2168 		 * - Combine the results into one large XPath query.
2169 		 * - Execute the query.
2170 		 */
2171 		if ((classes & PEC_QRY_SYSTEM) != 0) {
2172 			if (pool_build_xpath_buf(prov, src, PEC_SYSTEM, props,
2173 			    cb, PO_FALSE) == PO_FAIL) {
2174 				free_char_buf(cb);
2175 				return (NULL);
2176 			}
2177 		}
2178 		if ((classes & PEC_QRY_POOL) != 0) {
2179 			if (pool_build_xpath_buf(prov, src, PEC_POOL, props,
2180 			    cb, PO_FALSE) == PO_FAIL) {
2181 				free_char_buf(cb);
2182 				return (NULL);
2183 			}
2184 		}
2185 		if ((classes & PEC_QRY_RES_COMP) != 0) {
2186 			if (pool_build_xpath_buf(prov, src, PEC_RES_COMP, props,
2187 			    cb, PO_FALSE) == PO_FAIL) {
2188 				free_char_buf(cb);
2189 				return (NULL);
2190 			}
2191 		}
2192 		if ((classes & PEC_QRY_RES_AGG) != 0) {
2193 			if (pool_build_xpath_buf(prov, src, PEC_RES_AGG, props,
2194 			    cb, PO_FALSE) == PO_FAIL) {
2195 				free_char_buf(cb);
2196 				return (NULL);
2197 			}
2198 		}
2199 		if ((classes & PEC_QRY_COMP) != 0) {
2200 			if (pool_build_xpath_buf(prov, src, PEC_COMP, props,
2201 			    cb, PO_FALSE) == PO_FAIL) {
2202 				free_char_buf(cb);
2203 				return (NULL);
2204 			}
2205 		}
2206 	}
2207 	buf = strdup(cb->cb_buf);
2208 	free_char_buf(cb);
2209 	/*
2210 	 * Have a buffer at this point, that we can use
2211 	 */
2212 	if ((rs = pool_xml_result_set_alloc(conf)) == NULL) {
2213 		free(buf);
2214 		return (NULL);
2215 	}
2216 	/*
2217 	 * Set up the XPath Query
2218 	 */
2219 	if ((rs->pxr_ctx = xmlXPathNewContext(
2220 	    ((pool_xml_connection_t *)conf->pc_prov)->pxc_doc)) == NULL) {
2221 		free(buf);
2222 		(void) pool_xml_rs_close((pool_result_set_t *)rs);
2223 		pool_seterror(POE_DATASTORE);
2224 		return (NULL);
2225 	}
2226 	if (src == NULL)
2227 		rs->pxr_ctx->node = xmlDocGetRootElement
2228 		    (((pool_xml_connection_t *)conf->pc_prov)->pxc_doc);
2229 	else
2230 		rs->pxr_ctx->node = pxe->pxe_node;
2231 	/*
2232 	 * Select
2233 	 */
2234 	rs->pxr_path = xmlXPathEval(BAD_CAST buf, rs->pxr_ctx);
2235 	free(buf);
2236 	/*
2237 	 * Generate the result set and wrap the results as pool_elem_t
2238 	 */
2239 	if (rs->pxr_path->nodesetval->nodeNr == 0)
2240 		pool_seterror(POE_INVALID_SEARCH);
2241 	return ((pool_result_set_t *)rs);
2242 }
2243 
2244 /*
2245  * Build an XPath query buffer. This is complex and a little fragile, but
2246  * I'm trying to accomplish something complex with as little code as possible.
2247  * I wait the implementation of XMLQuery with baited breath...
2248  * Returns PO_SUCCESS/PO_FAIL
2249  */
2250 static int
pool_build_xpath_buf(pool_xml_connection_t * prov,const pool_elem_t * src,pool_elem_class_t class,pool_value_t * props[],char_buf_t * cb,int is_ref)2251 pool_build_xpath_buf(pool_xml_connection_t *prov, const pool_elem_t *src,
2252     pool_elem_class_t class, pool_value_t *props[], char_buf_t *cb, int is_ref)
2253 {
2254 	int i;
2255 	const char *ATTR_FMTS[] = {
2256 		"[ @%s=\"%llu\" ]",	/* POC_UINT */
2257 		"[ @%s=\"%lld\" ]",	/* POC_INT */
2258 		"[ @%s=\"%f\" ]",	/* POC_DOUBLE */
2259 		"[ @%s=\"%s\" ]",	/* POC_BOOL */
2260 		"[ @%s=\"%s\" ]",	/* POC_STRING */
2261 	};
2262 	const char *PROP_FMTS[] = {
2263 		"[ property[@name=\"%s\"][text()=\"%llu\"] ]",	/* POC_UINT */
2264 		"[ property[@name=\"%s\"][text()=\"%lld\"] ]",	/* POC_INT */
2265 		"[ property[@name=\"%s\"][text()=\"%f\"] ]",	/* POC_DOUBLE */
2266 		"[ property[@name=\"%s\"][text()=\"%s\"] ]",	/* POC_BOOL */
2267 		"[ property[@name=\"%s\"][text()=\"%s\"] ]"	/* POC_STRING */
2268 	};
2269 	const char **fmts;
2270 	int nprop;
2271 	const char *last_prop_name = NULL;
2272 	char *type_prefix = NULL;
2273 	int has_type = PO_FALSE;
2274 
2275 	if (is_ref == PO_FALSE) {
2276 		if (cb->cb_buf != NULL && strlen(cb->cb_buf) > 0)
2277 			(void) append_char_buf(cb, " |");
2278 		if (src != NULL)
2279 			(void) append_char_buf(cb, " ./");
2280 		else
2281 			(void) append_char_buf(cb, "//");
2282 		(void) append_char_buf(cb, element_class_tags[class]);
2283 	}
2284 	if (props == NULL || props[0] == NULL)
2285 		return (PO_SUCCESS);
2286 	for (nprop = 0; props[nprop] != NULL; nprop++)
2287 		/* Count properties */;
2288 	/*
2289 	 * Sort the attributes and properties by name.
2290 	 */
2291 	qsort(props, nprop, sizeof (pool_value_t *), prop_sort);
2292 	for (i = 0; i < nprop; i++) {
2293 		int is_attr = 0;
2294 		const char *prefix;
2295 		const char *prop_name;
2296 		uint64_t uval;
2297 		int64_t ival;
2298 		double dval;
2299 		uchar_t bval;
2300 		const char *sval;
2301 		pool_value_class_t pvc;
2302 
2303 		prop_name = pool_value_get_name(props[i]);
2304 		if ((prefix = is_a_known_prefix(class, prop_name)) != NULL) {
2305 			const char *attr_name;
2306 			/*
2307 			 * Possibly an attribute. Strip off the prefix.
2308 			 */
2309 			if (strcmp(prop_name, c_type) == 0) {
2310 				has_type = PO_TRUE;
2311 				attr_name = prop_name;
2312 			} else
2313 				attr_name = prop_name + strlen(prefix) + 1;
2314 			if (pool_is_xml_attr(prov->pxc_doc,
2315 			    element_class_tags[class], attr_name)) {
2316 				is_attr = 1;
2317 				prop_name = attr_name;
2318 				if (class == PEC_RES_COMP ||
2319 				    class == PEC_RES_AGG ||
2320 				    class == PEC_COMP) {
2321 					if (type_prefix != NULL)
2322 						free(type_prefix);
2323 					type_prefix = strdup(prefix);
2324 				}
2325 			}
2326 		}
2327 		if (is_attr)  {
2328 			fmts = ATTR_FMTS;
2329 		} else {
2330 			fmts = PROP_FMTS;
2331 		}
2332 		/*
2333 		 * Add attributes/properties to the search buffer
2334 		 */
2335 		switch ((pvc = pool_value_get_type(props[i]))) {
2336 		case POC_UINT:
2337 			(void) pool_value_get_uint64(props[i], &uval);
2338 			if (append_char_buf(cb, fmts[pvc], prop_name, uval)
2339 			    == PO_FAIL) {
2340 				free(type_prefix);
2341 				return (PO_FAIL);
2342 			}
2343 			break;
2344 		case POC_INT:
2345 			(void) pool_value_get_int64(props[i], &ival);
2346 			if (append_char_buf(cb, fmts[pvc], prop_name, ival)
2347 			    == PO_FAIL) {
2348 				free(type_prefix);
2349 				return (PO_FAIL);
2350 			}
2351 			break;
2352 		case POC_DOUBLE:
2353 			(void) pool_value_get_double(props[i], &dval);
2354 			if (append_char_buf(cb, fmts[pvc], prop_name, dval)
2355 			    == PO_FAIL) {
2356 				free(type_prefix);
2357 				return (PO_FAIL);
2358 			}
2359 			break;
2360 		case POC_BOOL:
2361 			(void) pool_value_get_bool(props[i], &bval);
2362 			if (append_char_buf(cb, fmts[pvc], prop_name,
2363 			    bval ? "true" : "false") == PO_FAIL) {
2364 				free(type_prefix);
2365 				return (PO_FAIL);
2366 			}
2367 			break;
2368 		case POC_STRING:
2369 			(void) pool_value_get_string(props[i], &sval);
2370 			if (append_char_buf(cb, fmts[pvc], prop_name, sval)
2371 			    == PO_FAIL) {
2372 				free(type_prefix);
2373 				return (PO_FAIL);
2374 			}
2375 			break;
2376 		default:
2377 			free(type_prefix);
2378 			pool_seterror(POE_INVALID_SEARCH);
2379 			return (PO_FAIL);
2380 		}
2381 		if (last_prop_name != NULL) {
2382 			const char *suffix1, *suffix2;
2383 			/*
2384 			 * Extra fiddling for namespaces
2385 			 */
2386 			suffix1 = strrchr(prop_name, '.');
2387 			suffix2 = strrchr(last_prop_name, '.');
2388 
2389 			if (suffix1 != NULL || suffix2 != NULL) {
2390 				if (suffix1 == NULL)
2391 					suffix1 = prop_name;
2392 				else
2393 					suffix1++;
2394 				if (suffix2 == NULL)
2395 					suffix2 = last_prop_name;
2396 				else
2397 					suffix2++;
2398 			} else {
2399 				suffix1 = prop_name;
2400 				suffix2 = last_prop_name;
2401 			}
2402 			if (strcmp(suffix1, suffix2) == 0) {
2403 				char *where = strrchr(cb->cb_buf, '[');
2404 				if (is_attr != PO_TRUE) {
2405 					/* repeat */
2406 					while (*--where != '[')
2407 						;
2408 					while (*--where != '[')
2409 						;
2410 				}
2411 				*(where - 1) = 'o';
2412 				*where = 'r';
2413 			}
2414 		}
2415 		last_prop_name = prop_name;
2416 	}
2417 	if (has_type == PO_FALSE) {
2418 		if (type_prefix) {
2419 			if (append_char_buf(cb, ATTR_FMTS[POC_STRING],
2420 			    c_type, type_prefix) == PO_FAIL) {
2421 				free(type_prefix);
2422 				return (PO_FAIL);
2423 			}
2424 		}
2425 	}
2426 	free(type_prefix);
2427 	return (PO_SUCCESS);
2428 }
2429 
2430 /*
2431  * Utility routine for use by quicksort. Assumes that the supplied data
2432  * are pool values and compares the names of the two pool values.
2433  * Returns an integer greater than, equal to, or less than 0.
2434  */
2435 static int
prop_sort(const void * a,const void * b)2436 prop_sort(const void *a, const void *b)
2437 {
2438 	pool_value_t **prop_a = (pool_value_t **)a;
2439 	pool_value_t **prop_b = (pool_value_t **)b;
2440 	const char *str_a;
2441 	const char *str_b;
2442 	const char *suffix1, *suffix2;
2443 
2444 	str_a = pool_value_get_name(*prop_a);
2445 	str_b = pool_value_get_name(*prop_b);
2446 	/*
2447 	 * Extra fiddling for namespaces
2448 	 */
2449 	suffix1 = strrchr(str_a, '.');
2450 	suffix2 = strrchr(str_b, '.');
2451 
2452 	if (suffix1 != NULL || suffix2 != NULL) {
2453 		if (suffix1 == NULL)
2454 			suffix1 = str_a;
2455 		else
2456 			suffix1++;
2457 		if (suffix2 == NULL)
2458 			suffix2 = str_b;
2459 		else
2460 			suffix2++;
2461 	} else {
2462 		suffix1 = str_a;
2463 		suffix2 = str_b;
2464 	}
2465 	return (strcmp(suffix1, suffix2));
2466 }
2467 
2468 /*
2469  * Order the elements by (ref_id)
2470  */
2471 
2472 /*
2473  * Returns PO_TRUE/PO_FALSE to indicate whether the supplied path exists on the
2474  * system. It is assumed that the supplied path is in URL format and represents
2475  * a file and so file:// is stripped from the start of the search.
2476  */
2477 static int
dtd_exists(const char * path)2478 dtd_exists(const char *path)
2479 {
2480 	struct stat buf;
2481 
2482 	if (strstr(path, "file://") != path)
2483 		return (PO_FALSE);
2484 
2485 	if (path[7] == 0)
2486 		return (PO_FALSE);
2487 
2488 	if (stat(&path[7], &buf) == 0)
2489 		return (PO_TRUE);
2490 	return (PO_FALSE);
2491 }
2492 
2493 /*
2494  * Build the dtype structures to accelerate data type lookup operations. The
2495  * purpose is to avoid expensive XML manipulations on data which will not
2496  * change over the life of a library invocation. It is designed to be invoked
2497  * once from the library init function.
2498  */
2499 static void
build_dtype_accelerator(void)2500 build_dtype_accelerator(void)
2501 {
2502 	xmlDtdPtr dtd;
2503 	const xmlChar *elem_list[ELEM_TYPE_COUNT] = {
2504 		BAD_CAST "res_comp",
2505 		BAD_CAST "res_agg",
2506 		BAD_CAST "comp",
2507 		BAD_CAST "pool",
2508 		BAD_CAST "property",
2509 		BAD_CAST "system" };
2510 	int i;
2511 
2512 	if (_libpool_xml_initialised == PO_TRUE)
2513 		return;
2514 
2515 	/* Load up the d-type data for each element */
2516 	/*
2517 	 * Store data type information in nested lists
2518 	 * Top level list contains attribute declaration pointers which
2519 	 * can be used to match with supplied nodes.
2520 	 * Second level list contains attribute type information for each
2521 	 * element declaration
2522 	 */
2523 	/*
2524 	 * Unfortunately, there's no easy way to get a list of all DTD
2525 	 * element descriptions as there is no libxml API to do this (they
2526 	 * are stored in a hash, which I guess is why). Explicitly seek
2527 	 * for descriptions for elements that are known to hold an a-dtype
2528 	 * attribute and build accelerators for those elements.
2529 	 * If the DTD changes, the library may have to change as well now,
2530 	 * since this code makes explicit assumptions about which elements
2531 	 * contain a-dtype information.
2532 	 */
2533 
2534 	if ((dtd = xmlParseDTD(BAD_CAST "-//Sun Microsystems Inc//DTD Resource"
2535 	    " Management All//EN", BAD_CAST dtd_location)) == NULL)
2536 		return;
2537 	for (i = 0; i < ELEM_TYPE_COUNT; i++) {
2538 		xmlElementPtr elem;
2539 		xmlAttributePtr attr;
2540 
2541 		if ((elem = xmlGetDtdElementDesc(dtd, elem_list[i])) == NULL)
2542 			return;
2543 		elem_tbl[i].ett_elem = xmlStrdup(elem->name);
2544 		/* Walk the list of attributes looking for a-dtype */
2545 		for (attr = elem->attributes; attr != NULL;
2546 		    attr = attr->nexth) {
2547 			if (strcmp((const char *)attr->name, c_a_dtype) == 0) {
2548 				/*
2549 				 * Allocate a dtype_tbl_t
2550 				 */
2551 				elem_tbl[i].ett_dtype =
2552 				    build_dtype_tbl(attr->defaultValue);
2553 				/* This could have returned NULL */
2554 			}
2555 		}
2556 	}
2557 	xmlFreeDtd(dtd);
2558 }
2559 
2560 /*
2561  * build_dtype_tbl() parses the supplied data and returns an array (max size
2562  * of 10, increase if required) of dtype_tbl_t structures holding data type
2563  * information for an element. The supplied data is assumed to be in "a-dtype"
2564  * format. The dtype_tbl_t array is NULL terminated, which is why space for
2565  * 11 members is allocated.
2566  */
2567 static dtype_tbl_t
build_dtype_tbl(const xmlChar * rawdata)2568 (*build_dtype_tbl(const xmlChar *rawdata))[]
2569 {
2570 	char *tok;
2571 	char *lasts;
2572 	dtype_tbl_t (*tbl)[];
2573 	int j = 0;
2574 	xmlChar *data;
2575 	const int max_attr = 11; /* Not more than 10 types per element */
2576 
2577 	/*
2578 	 * Parse the supplied data, assumed to be in a-dtype format, and
2579 	 * generate a lookup table which is indexed by the name and contains
2580 	 * the data type
2581 	 */
2582 	if (rawdata == NULL)
2583 	return (NULL);
2584 	if ((data = xmlStrdup(rawdata)) == NULL)
2585 	return (NULL);
2586 	if ((tbl = calloc(max_attr, sizeof (dtype_tbl_t))) == NULL) {
2587 		xmlFree(data);
2588 		return (NULL);
2589 	}
2590 	for (tok = strtok_r((char *)data, "	 ", &lasts); tok != NULL;
2591 	    tok = strtok_r(NULL, "	 ", &lasts)) {
2592 		    int i;
2593 		    (*tbl)[j].dt_name  = xmlStrdup(BAD_CAST tok);
2594 		    if ((tok = strtok_r(NULL, "	 ", &lasts)) == NULL) {
2595 			    int k = j;
2596 			    for (j = 0; j < k; j++)
2597 				    free((*tbl)[j].dt_name);
2598 			    pool_seterror(POE_DATASTORE);
2599 			    xmlFree(data);
2600 			    free(tbl);
2601 			    return (NULL);
2602 		    }
2603 		    for (i = 0; i < (sizeof (data_type_tags) /
2604 			sizeof (data_type_tags[0])); i++) {
2605 				if (strcmp(tok, data_type_tags[i]) == 0)
2606 				(*tbl)[j++].dt_type = i;
2607 			}
2608 		    if (j == max_attr) { /* too many attributes, bail out */
2609 			    for (j = 0; j < max_attr; j++)
2610 			    free((*tbl)[j].dt_name);
2611 			    free(tbl);
2612 			    xmlFree(data);
2613 			    return (NULL);
2614 		    }
2615 	    }
2616 	(*tbl)[j].dt_name = NULL; /* Terminate the table */
2617 	xmlFree(data);
2618 	return (tbl);
2619 }
2620 
2621 /*
2622  * get_fast_dtype() finds the data type for a supplied attribute name on a
2623  * supplied node. This is called get_fast_dtype() because it uses the cached
2624  * data type information created at library initialisation.
2625  */
2626 static int
get_fast_dtype(xmlNodePtr node,xmlChar * name)2627 get_fast_dtype(xmlNodePtr node, xmlChar *name)
2628 {
2629 	int i;
2630 	xmlElementPtr elem;
2631 
2632 	if ((elem = xmlGetDtdElementDesc(node->doc->extSubset, node->name))
2633 	    == NULL) {
2634 		pool_seterror(POE_BADPARAM);
2635 		return (POC_INVAL);
2636 	}
2637 
2638 	for (i = 0; i < ELEM_TYPE_COUNT; i++) {
2639 		if (xmlStrcmp(elem_tbl[i].ett_elem, elem->name) == 0) {
2640 			dtype_tbl_t (*tbl)[] = elem_tbl[i].ett_dtype;
2641 			int j = 0;
2642 
2643 			if (tbl == NULL)
2644 				break;
2645 			for (j = 0; (*tbl)[j].dt_name != NULL; j++)
2646 				if (xmlStrcmp(name, (*tbl)[j].dt_name) == 0)
2647 					return ((*tbl)[j].dt_type); /* found */
2648 			break; /* if we didn't find it in the elem, break */
2649 		}
2650 	}
2651 	/* If we can't find it, say it's a string */
2652 	return (POC_STRING);
2653 }
2654 
2655 /*
2656  * pool_xml_parse_document() parses the file associated with a supplied
2657  * configuration to regenerate the runtime representation. The supplied
2658  * configuration must reference an already opened file and this is used
2659  * to generate the XML representation via the configuration provider's
2660  * pxc_doc member.
2661  * size must be >=4 in order for "content encoding detection" to work.
2662  */
2663 static int
pool_xml_parse_document(pool_conf_t * conf)2664 pool_xml_parse_document(pool_conf_t *conf)
2665 {
2666 	int res;
2667 	char chars[PAGE_READ_SIZE];
2668 	struct stat f_stat;
2669 	xmlParserCtxtPtr ctxt;
2670 	size_t size;
2671 	pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov;
2672 	xmlNodePtr root;
2673 	pool_resource_t **rsl;
2674 	uint_t nelem;
2675 	int i;
2676 
2677 	if (fstat(fileno(prov->pxc_file), &f_stat) == -1) {
2678 		pool_seterror(POE_SYSTEM);
2679 		return (PO_FAIL);
2680 	}
2681 
2682 	if (f_stat.st_size == 0) {
2683 		pool_seterror(POE_INVALID_CONF);
2684 		return (PO_FAIL);
2685 	} else
2686 		size = f_stat.st_size < 4 ? 4 : PAGE_READ_SIZE;
2687 
2688 	res = fread(chars, 1, size, prov->pxc_file);
2689 
2690 	if (res >= 4) {
2691 		xmlValidCtxtPtr cvp;
2692 
2693 		if ((ctxt = xmlCreatePushParserCtxt(NULL, NULL,
2694 		    chars, res, conf->pc_location)) == NULL) {
2695 			pool_seterror(POE_INVALID_CONF);
2696 			return (PO_FAIL);
2697 		}
2698 
2699 		xmlCtxtUseOptions(ctxt,
2700 		    XML_PARSE_DTDLOAD | XML_PARSE_DTDVALID |
2701 		    XML_PARSE_NOBLANKS);
2702 
2703 		while ((res = fread(chars, 1, size, prov->pxc_file)) > 0) {
2704 			if (xmlParseChunk(ctxt, chars, res, 0) != 0) {
2705 				xmlFreeParserCtxt(ctxt);
2706 				pool_seterror(POE_INVALID_CONF);
2707 				return (PO_FAIL);
2708 			}
2709 		}
2710 		if (xmlParseChunk(ctxt, chars, 0, 1) != 0) {
2711 			xmlFreeParserCtxt(ctxt);
2712 			pool_seterror(POE_INVALID_CONF);
2713 			return (PO_FAIL);
2714 		}
2715 
2716 		if ((cvp = xmlNewValidCtxt()) == NULL) {
2717 			pool_seterror(POE_INVALID_CONF);
2718 			return (PO_FAIL);
2719 		}
2720 		cvp->error    = pool_error_func;
2721 		cvp->warning  = pool_error_func;
2722 
2723 		if (xmlValidateDocument(cvp, ctxt->myDoc) == 0) {
2724 			xmlFreeValidCtxt(cvp);
2725 			xmlFreeParserCtxt(ctxt);
2726 			pool_seterror(POE_INVALID_CONF);
2727 			return (PO_FAIL);
2728 		}
2729 		prov->pxc_doc = ctxt->myDoc;
2730 		xmlFreeValidCtxt(cvp);
2731 		xmlFreeParserCtxt(ctxt);
2732 	}
2733 	if (prov->pxc_doc == NULL) {
2734 		pool_seterror(POE_INVALID_CONF);
2735 		return (PO_FAIL);
2736 	}
2737 	prov->pxc_doc->_private = conf;
2738 
2739 	/* Get the root element */
2740 	if ((root = xmlDocGetRootElement(prov->pxc_doc)) == NULL) {
2741 		pool_seterror(POE_INVALID_CONF);
2742 		return (PO_FAIL);
2743 	}
2744 	/*
2745 	 * Ensure that the parsed tree has been contained within
2746 	 * our shadow tree.
2747 	 */
2748 	if (create_shadow(root) != PO_SUCCESS) {
2749 		pool_seterror(POE_INVALID_CONF);
2750 		return (PO_FAIL);
2751 	}
2752 
2753 	if (pool_xml_validate(conf, POV_STRICT) != PO_SUCCESS) {
2754 		return (PO_FAIL);
2755 	}
2756 	/*
2757 	 * For backwards compatibility with S9, make sure that all
2758 	 * resources have a size and that it is correct.
2759 	 */
2760 	if ((rsl = pool_query_resources(conf, &nelem, NULL)) != NULL) {
2761 		pool_value_t val = POOL_VALUE_INITIALIZER;
2762 		for (i = 0; i < nelem; i++) {
2763 			if (pool_get_ns_property(TO_ELEM(rsl[i]), c_size_prop,
2764 			    &val) != POC_UINT) {
2765 				pool_component_t **cs;
2766 				uint_t size;
2767 				if ((cs = pool_query_resource_components(conf,
2768 				    rsl[i], &size, NULL)) != NULL) {
2769 					free(cs);
2770 					pool_value_set_uint64(&val, size);
2771 				} else
2772 					pool_value_set_uint64(&val, 0);
2773 				if (pool_put_any_ns_property(TO_ELEM(rsl[i]),
2774 				    c_size_prop, &val)  != PO_SUCCESS) {
2775 					free(rsl);
2776 					return (PO_FAIL);
2777 				}
2778 			}
2779 		}
2780 		free(rsl);
2781 	}
2782 	return (PO_SUCCESS);
2783 }
2784