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