1fa9e4066Sahrens /*
2fa9e4066Sahrens  * CDDL HEADER START
3fa9e4066Sahrens  *
4fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5*32f884e0Stalley  * Common Development and Distribution License (the "License").
6*32f884e0Stalley  * You may not use this file except in compliance with the License.
7fa9e4066Sahrens  *
8fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens  * See the License for the specific language governing permissions
11fa9e4066Sahrens  * and limitations under the License.
12fa9e4066Sahrens  *
13fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens  *
19fa9e4066Sahrens  * CDDL HEADER END
20fa9e4066Sahrens  */
21*32f884e0Stalley 
22fa9e4066Sahrens /*
23*32f884e0Stalley  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24fa9e4066Sahrens  * Use is subject to license terms.
25fa9e4066Sahrens  */
26fa9e4066Sahrens 
27fa9e4066Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
28fa9e4066Sahrens 
29fa9e4066Sahrens #include "libzfs_jni_diskmgt.h"
30fa9e4066Sahrens #include "libzfs_jni_util.h"
31fa9e4066Sahrens #include <strings.h>
32fa9e4066Sahrens #include <libzfs.h>
33fa9e4066Sahrens #include <sys/mnttab.h>
34fa9e4066Sahrens 
35fa9e4066Sahrens /*
36fa9e4066Sahrens  * Function prototypes
37fa9e4066Sahrens  */
38fa9e4066Sahrens 
39fa9e4066Sahrens static char *get_device_name(dm_descriptor_t device, int *error);
40fa9e4066Sahrens static dmgt_disk_t *get_disk(dm_descriptor_t disk, int *error);
41fa9e4066Sahrens static char **get_disk_aliases(dm_descriptor_t disk, char *name, int *error);
42fa9e4066Sahrens static int get_disk_online(dm_descriptor_t disk, int *error);
43fa9e4066Sahrens static void remove_slice_from_list(dmgt_slice_t **slices, int index);
44fa9e4066Sahrens static dmgt_slice_t **get_disk_slices(dm_descriptor_t media,
45fa9e4066Sahrens     const char *name, uint32_t blocksize, int *error);
46fa9e4066Sahrens static dmgt_slice_t **get_disk_usable_slices(dm_descriptor_t media,
47fa9e4066Sahrens     const char *name, uint32_t blocksize, int *in_use, int *error);
48fa9e4066Sahrens static void get_disk_size(dm_descriptor_t media, char *name,
49fa9e4066Sahrens     uint64_t *size, uint32_t *blocksize, int *error);
50fa9e4066Sahrens static void get_slice_use(dm_descriptor_t slice, char *name,
51fa9e4066Sahrens     char **used_name, char **used_by, int *error);
52fa9e4066Sahrens static dmgt_slice_t *get_slice(
53fa9e4066Sahrens     dm_descriptor_t slice, uint32_t blocksize, int *error);
54fa9e4066Sahrens static void handle_error(const char *format, ...);
55*32f884e0Stalley static int slice_in_use(dmgt_slice_t *slice, int *error);
56fa9e4066Sahrens static int slice_too_small(dmgt_slice_t *slice);
57fa9e4066Sahrens 
58fa9e4066Sahrens /*
59fa9e4066Sahrens  * Static data
60fa9e4066Sahrens  */
61fa9e4066Sahrens 
62fa9e4066Sahrens static void (*error_func)(const char *, va_list);
63fa9e4066Sahrens 
64fa9e4066Sahrens /*
65fa9e4066Sahrens  * Static functions
66fa9e4066Sahrens  */
67fa9e4066Sahrens 
68fa9e4066Sahrens static char *
69fa9e4066Sahrens get_device_name(dm_descriptor_t device, int *error)
70fa9e4066Sahrens {
71fa9e4066Sahrens 	char *dup;
72fa9e4066Sahrens 	char *name;
73fa9e4066Sahrens 
74fa9e4066Sahrens 	*error = 0;
75fa9e4066Sahrens 	name = dm_get_name(device, error);
76fa9e4066Sahrens 	if (*error) {
77fa9e4066Sahrens 		handle_error("could not determine name of device");
78fa9e4066Sahrens 	} else {
79fa9e4066Sahrens 		dup = strdup(name);
80fa9e4066Sahrens 		if (dup == NULL) {
81fa9e4066Sahrens 			handle_error("out of memory");
82fa9e4066Sahrens 			*error = -1;
83fa9e4066Sahrens 		}
84fa9e4066Sahrens 
85fa9e4066Sahrens 		dm_free_name(name);
86fa9e4066Sahrens 	}
87fa9e4066Sahrens 
88fa9e4066Sahrens 	return (dup);
89fa9e4066Sahrens }
90fa9e4066Sahrens 
91fa9e4066Sahrens /*
92fa9e4066Sahrens  * Gets a dmgt_disk_t for the given disk dm_descriptor_t.
93fa9e4066Sahrens  *
94fa9e4066Sahrens  * Results:
95fa9e4066Sahrens  *
96fa9e4066Sahrens  *  1. Success: error is set to 0 and a dmgt_disk_t is returned
97fa9e4066Sahrens  *
98fa9e4066Sahrens  *  2. Failure: error is set to -1 and NULL is returned
99fa9e4066Sahrens  */
100fa9e4066Sahrens static dmgt_disk_t *
101fa9e4066Sahrens get_disk(dm_descriptor_t disk, int *error)
102fa9e4066Sahrens {
103fa9e4066Sahrens 	dmgt_disk_t *dp;
104fa9e4066Sahrens 	*error = 0;
105fa9e4066Sahrens 
106fa9e4066Sahrens 	dp = (dmgt_disk_t *)calloc(1, sizeof (dmgt_disk_t));
107fa9e4066Sahrens 	if (dp == NULL) {
108fa9e4066Sahrens 		handle_error("out of memory");
109fa9e4066Sahrens 		*error = -1;
110fa9e4066Sahrens 	} else {
111fa9e4066Sahrens 
112fa9e4066Sahrens 		/* Get name */
113fa9e4066Sahrens 		dp->name = get_device_name(disk, error);
114fa9e4066Sahrens 		if (!*error) {
115fa9e4066Sahrens 
116fa9e4066Sahrens 			/* Get aliases */
117fa9e4066Sahrens 			dp->aliases = get_disk_aliases(disk, dp->name, error);
118fa9e4066Sahrens 			if (!*error) {
119fa9e4066Sahrens 
120fa9e4066Sahrens 				/* Get media */
121fa9e4066Sahrens 				dm_descriptor_t *media =
122fa9e4066Sahrens 				    dm_get_associated_descriptors(disk,
123fa9e4066Sahrens 					DM_MEDIA, error);
124fa9e4066Sahrens 				if (*error != 0 || media == NULL ||
125fa9e4066Sahrens 				    *media == NULL) {
126fa9e4066Sahrens 					handle_error(
127fa9e4066Sahrens 					    "could not get media from disk %s",
128fa9e4066Sahrens 					    dp->name);
129fa9e4066Sahrens 					*error = -1;
130fa9e4066Sahrens 				} else {
131fa9e4066Sahrens 					/* Get size */
132fa9e4066Sahrens 					get_disk_size(media[0], dp->name,
133fa9e4066Sahrens 					    &(dp->size), &(dp->blocksize),
134fa9e4066Sahrens 					    error);
135fa9e4066Sahrens 					if (!*error) {
136fa9e4066Sahrens 						/* Get free slices */
137fa9e4066Sahrens 						dp->slices =
138fa9e4066Sahrens 						    get_disk_usable_slices(
139fa9e4066Sahrens 							media[0], dp->name,
140fa9e4066Sahrens 							dp->blocksize,
141fa9e4066Sahrens 							&(dp->in_use), error);
142fa9e4066Sahrens 					}
143fa9e4066Sahrens 					dm_free_descriptors(media);
144fa9e4066Sahrens 				}
145fa9e4066Sahrens 			}
146fa9e4066Sahrens 		}
147fa9e4066Sahrens 	}
148fa9e4066Sahrens 
149fa9e4066Sahrens 	if (*error) {
150dc307942Stalley 		/* Normalize error */
151dc307942Stalley 		*error = -1;
152fa9e4066Sahrens 
153fa9e4066Sahrens 		if (dp != NULL) {
154fa9e4066Sahrens 			dmgt_free_disk(dp);
155fa9e4066Sahrens 			dp = NULL;
156fa9e4066Sahrens 		}
157fa9e4066Sahrens 	}
158fa9e4066Sahrens 
159fa9e4066Sahrens 	return (dp);
160fa9e4066Sahrens }
161fa9e4066Sahrens 
162fa9e4066Sahrens static char **
163fa9e4066Sahrens get_disk_aliases(dm_descriptor_t disk, char *name, int *error)
164fa9e4066Sahrens {
165fa9e4066Sahrens 	char **names = NULL;
166fa9e4066Sahrens 	dm_descriptor_t *aliases;
167fa9e4066Sahrens 
168fa9e4066Sahrens 	*error = 0;
169fa9e4066Sahrens 	aliases = dm_get_associated_descriptors(disk, DM_ALIAS, error);
170fa9e4066Sahrens 	if (*error || aliases == NULL) {
171fa9e4066Sahrens 		*error = -1;
172fa9e4066Sahrens 		handle_error("could not get aliases for disk %s", name);
173fa9e4066Sahrens 	} else {
174fa9e4066Sahrens 
175fa9e4066Sahrens 		int j;
176fa9e4066Sahrens 
177fa9e4066Sahrens 		/* Count aliases */
178fa9e4066Sahrens 		for (j = 0; aliases[j] != NULL; j++);
179fa9e4066Sahrens 
180fa9e4066Sahrens 		names = (char **)calloc(j + 1, sizeof (char *));
181fa9e4066Sahrens 		if (names == NULL) {
182fa9e4066Sahrens 			*error = -1;
183fa9e4066Sahrens 			handle_error("out of memory");
184fa9e4066Sahrens 		} else {
185fa9e4066Sahrens 
186fa9e4066Sahrens 			/* For each alias... */
187fa9e4066Sahrens 			for (j = 0; *error == 0 && aliases[j] != NULL; j++) {
188fa9e4066Sahrens 
189fa9e4066Sahrens 				dm_descriptor_t alias = aliases[j];
190fa9e4066Sahrens 				char *aname = dm_get_name(alias, error);
191fa9e4066Sahrens 				if (*error) {
192fa9e4066Sahrens 					handle_error("could not get alias %d "
193fa9e4066Sahrens 					    "for disk %s", (j + 1), name);
194fa9e4066Sahrens 				} else {
195fa9e4066Sahrens 					names[j] = strdup(aname);
196fa9e4066Sahrens 					if (names[j] == NULL) {
197fa9e4066Sahrens 						*error = -1;
198fa9e4066Sahrens 						handle_error("out of memory");
199fa9e4066Sahrens 					}
200fa9e4066Sahrens 
201fa9e4066Sahrens 					dm_free_name(aname);
202fa9e4066Sahrens 				}
203fa9e4066Sahrens 			}
204fa9e4066Sahrens 		}
205fa9e4066Sahrens 
206fa9e4066Sahrens 		dm_free_descriptors(aliases);
207fa9e4066Sahrens 	}
208fa9e4066Sahrens 
209fa9e4066Sahrens 	if (*error && names != NULL) {
210fa9e4066Sahrens 		/* Free previously-allocated names */
211c8e9ed14Stalley 		zjni_free_array((void **)names, free);
212fa9e4066Sahrens 	}
213fa9e4066Sahrens 
214fa9e4066Sahrens 	return (names);
215fa9e4066Sahrens }
216fa9e4066Sahrens 
217fa9e4066Sahrens static int
218fa9e4066Sahrens get_disk_online(dm_descriptor_t disk, int *error)
219fa9e4066Sahrens {
220fa9e4066Sahrens 	uint32_t status = 0;
221fa9e4066Sahrens 
222fa9e4066Sahrens 	nvlist_t *attrs;
223fa9e4066Sahrens 	*error = 0;
224fa9e4066Sahrens 	attrs = dm_get_attributes(disk, error);
225fa9e4066Sahrens 	if (*error) {
226fa9e4066Sahrens 		handle_error("could not get disk attributes for disk");
227fa9e4066Sahrens 	} else {
228fa9e4066Sahrens 
229fa9e4066Sahrens 		/* Try to get the status */
230fa9e4066Sahrens 		nvpair_t *match = zjni_nvlist_walk_nvpair(
231fa9e4066Sahrens 		    attrs, DM_STATUS, DATA_TYPE_UINT32, NULL);
232fa9e4066Sahrens 
233fa9e4066Sahrens 		if (match == NULL || nvpair_value_uint32(match, &status)) {
234fa9e4066Sahrens 
235fa9e4066Sahrens 			handle_error("could not get status of disk");
236fa9e4066Sahrens 			*error = 1;
237fa9e4066Sahrens 		}
238fa9e4066Sahrens 
239fa9e4066Sahrens 		nvlist_free(attrs);
240fa9e4066Sahrens 	}
241fa9e4066Sahrens 
242fa9e4066Sahrens 	return (status != 0);
243fa9e4066Sahrens }
244fa9e4066Sahrens 
245fa9e4066Sahrens /*
246fa9e4066Sahrens  * Gets the slices for the given disk.
247fa9e4066Sahrens  *
248fa9e4066Sahrens  * Results:
249fa9e4066Sahrens  *
250fa9e4066Sahrens  *  1. Success: error is set to 0 and slices are returned
251fa9e4066Sahrens  *
252fa9e4066Sahrens  *  2. Failure: error is set to -1 and NULL is returned
253fa9e4066Sahrens  */
254fa9e4066Sahrens static dmgt_slice_t **
255fa9e4066Sahrens get_disk_slices(dm_descriptor_t media, const char *name, uint32_t blocksize,
256fa9e4066Sahrens     int *error)
257fa9e4066Sahrens {
258fa9e4066Sahrens 	dm_descriptor_t *slices;
259fa9e4066Sahrens 	dmgt_slice_t **sap = NULL;
260fa9e4066Sahrens 
261fa9e4066Sahrens 	*error = 0;
262fa9e4066Sahrens 	slices = dm_get_associated_descriptors(media, DM_SLICE, error);
263fa9e4066Sahrens 	if (*error != 0) {
264fa9e4066Sahrens 		handle_error("could not get slices of disk %s", name);
265fa9e4066Sahrens 	} else {
266fa9e4066Sahrens 		int j;
267fa9e4066Sahrens 		int nslices = 0;
268fa9e4066Sahrens 
269fa9e4066Sahrens 		/* For each slice... */
270fa9e4066Sahrens 		for (j = 0; *error == 0 &&
271fa9e4066Sahrens 		    slices != NULL && slices[j] != NULL; j++) {
272fa9e4066Sahrens 
273fa9e4066Sahrens 			/* Get slice */
274fa9e4066Sahrens 			dmgt_slice_t *slice =
275fa9e4066Sahrens 			    get_slice(slices[j], blocksize, error);
276fa9e4066Sahrens 			if (!*error) {
277fa9e4066Sahrens 
278*32f884e0Stalley 				dmgt_slice_t **mem =
279*32f884e0Stalley 				    (dmgt_slice_t **)realloc(sap,
280fa9e4066Sahrens 				    (nslices + 2) * sizeof (dmgt_slice_t *));
281*32f884e0Stalley 
282*32f884e0Stalley 				if (mem == NULL) {
283fa9e4066Sahrens 					handle_error("out of memory");
284fa9e4066Sahrens 					*error = -1;
285fa9e4066Sahrens 				} else {
286fa9e4066Sahrens 
287*32f884e0Stalley 					sap = mem;
288*32f884e0Stalley 
289fa9e4066Sahrens 					/* NULL-terminated array */
290fa9e4066Sahrens 					sap[nslices] = slice;
291fa9e4066Sahrens 					sap[nslices + 1] = NULL;
292fa9e4066Sahrens 
293fa9e4066Sahrens 					nslices++;
294fa9e4066Sahrens 				}
295fa9e4066Sahrens 			}
296fa9e4066Sahrens 		}
297fa9e4066Sahrens 
298fa9e4066Sahrens 		dm_free_descriptors(slices);
299fa9e4066Sahrens 	}
300fa9e4066Sahrens 
301fa9e4066Sahrens 	if (*error) {
302fa9e4066Sahrens 		/* Normalize error */
303fa9e4066Sahrens 		*error = -1;
304fa9e4066Sahrens 
305*32f884e0Stalley 		if (sap != NULL) {
306*32f884e0Stalley 			zjni_free_array((void **)sap,
307*32f884e0Stalley 			    (zjni_free_f)dmgt_free_slice);
308*32f884e0Stalley 			sap = NULL;
309*32f884e0Stalley 		}
310fa9e4066Sahrens 	}
311fa9e4066Sahrens 
312fa9e4066Sahrens 	return (sap);
313fa9e4066Sahrens }
314fa9e4066Sahrens 
315fa9e4066Sahrens static void
316fa9e4066Sahrens remove_slice_from_list(dmgt_slice_t **slices, int index)
317fa9e4066Sahrens {
318fa9e4066Sahrens 	int i;
319fa9e4066Sahrens 	for (i = index; slices[i] != NULL; i++) {
320fa9e4066Sahrens 		slices[i] = slices[i + 1];
321fa9e4066Sahrens 	}
322fa9e4066Sahrens }
323fa9e4066Sahrens 
324fa9e4066Sahrens static int
325fa9e4066Sahrens slices_overlap(dmgt_slice_t *slice1, dmgt_slice_t *slice2)
326fa9e4066Sahrens {
327fa9e4066Sahrens 
328fa9e4066Sahrens 	uint64_t start1 = slice1->start;
329fa9e4066Sahrens 	uint64_t end1 = start1 + slice1->size - 1;
330fa9e4066Sahrens 	uint64_t start2 = slice2->start;
331c8e9ed14Stalley 	uint64_t end2 = start2 + slice2->size - 1;
332fa9e4066Sahrens 
333fa9e4066Sahrens 	int overlap = (start2 <= end1 && start1 <= end2);
334fa9e4066Sahrens 
335fa9e4066Sahrens #ifdef DEBUG
336fa9e4066Sahrens 	if (overlap) {
337fa9e4066Sahrens 		(void) fprintf(stderr, "can't use %s: overlaps with %s\n",
338fa9e4066Sahrens 		    slice2->name, slice1->name);
339fa9e4066Sahrens 		(void) fprintf(stderr, "  1: start: %llu - %llu\n",
340fa9e4066Sahrens 		    (unsigned long long)start1, (unsigned long long)end1);
341fa9e4066Sahrens 		(void) fprintf(stderr, "  2: start: %llu - %llu\n",
342fa9e4066Sahrens 		    (unsigned long long)start2, (unsigned long long)end2);
343fa9e4066Sahrens 	}
344fa9e4066Sahrens #endif
345fa9e4066Sahrens 
346fa9e4066Sahrens 	return (overlap);
347fa9e4066Sahrens }
348fa9e4066Sahrens 
349fa9e4066Sahrens /*
350fa9e4066Sahrens  * Gets the slices for the given disk.
351fa9e4066Sahrens  *
352fa9e4066Sahrens  * Results:
353fa9e4066Sahrens  *
354fa9e4066Sahrens  *  1. Success: error is set to 0 and slices are returned
355fa9e4066Sahrens  *
356fa9e4066Sahrens  *  2. Failure: error is set to -1 and NULL is returned
357fa9e4066Sahrens  */
358fa9e4066Sahrens static dmgt_slice_t **
359fa9e4066Sahrens get_disk_usable_slices(dm_descriptor_t media, const char *name,
360fa9e4066Sahrens     uint32_t blocksize, int *in_use, int *error)
361fa9e4066Sahrens {
362fa9e4066Sahrens 	dmgt_slice_t **slices = get_disk_slices(media, name, blocksize, error);
363*32f884e0Stalley 	if (*error) {
364*32f884e0Stalley 		slices = NULL;
365*32f884e0Stalley 	}
366fa9e4066Sahrens 
367fa9e4066Sahrens 	*in_use = 0;
368fa9e4066Sahrens 
369*32f884e0Stalley 	if (slices != NULL) {
370fa9e4066Sahrens 		int i, nslices;
371fa9e4066Sahrens 
372fa9e4066Sahrens 		for (nslices = 0; slices[nslices] != NULL; nslices++);
373fa9e4066Sahrens 
374fa9e4066Sahrens 		/* Prune slices based on use */
375fa9e4066Sahrens 		for (i = nslices - 1; i >= 0; i--) {
376fa9e4066Sahrens 			dmgt_slice_t *slice = slices[i];
377*32f884e0Stalley 			int s_in_use;
378*32f884e0Stalley 
379*32f884e0Stalley 			/*
380*32f884e0Stalley 			 * Slice at this index could be NULL if
381*32f884e0Stalley 			 * removed in earlier iteration
382*32f884e0Stalley 			 */
383fa9e4066Sahrens 			if (slice == NULL) {
384fa9e4066Sahrens 				continue;
385fa9e4066Sahrens 			}
386fa9e4066Sahrens 
387*32f884e0Stalley 			s_in_use = slice_in_use(slice, error);
388*32f884e0Stalley 			if (*error) {
389*32f884e0Stalley 			    break;
390*32f884e0Stalley 			}
391*32f884e0Stalley 
392*32f884e0Stalley 			if (s_in_use) {
393fa9e4066Sahrens 				int j;
394fa9e4066Sahrens 				remove_slice_from_list(slices, i);
395fa9e4066Sahrens 
396*32f884e0Stalley 				/* Disk is in use */
397fa9e4066Sahrens 				*in_use = 1;
398fa9e4066Sahrens 
399fa9e4066Sahrens 				/*
400fa9e4066Sahrens 				 * Remove any slice that overlaps with this
401fa9e4066Sahrens 				 * in-use slice
402fa9e4066Sahrens 				 */
403fa9e4066Sahrens 				for (j = nslices - 1; j >= 0; j--) {
404*32f884e0Stalley 					dmgt_slice_t *slice2 = slices[j];
405*32f884e0Stalley 
406*32f884e0Stalley 					if (slice2 != NULL &&
407*32f884e0Stalley 					    slices_overlap(slice, slice2)) {
408fa9e4066Sahrens 						remove_slice_from_list(slices,
409fa9e4066Sahrens 						    j);
410*32f884e0Stalley 						dmgt_free_slice(slice2);
411fa9e4066Sahrens 					}
412fa9e4066Sahrens 				}
413*32f884e0Stalley 
414*32f884e0Stalley 				dmgt_free_slice(slice);
415*32f884e0Stalley 			} else if (slice_too_small(slice)) {
416*32f884e0Stalley 				remove_slice_from_list(slices, i);
417*32f884e0Stalley 				dmgt_free_slice(slice);
418dc307942Stalley 			}
419fa9e4066Sahrens 		}
420fa9e4066Sahrens 	}
421fa9e4066Sahrens 
422*32f884e0Stalley 	if (*error) {
423*32f884e0Stalley 		/* Normalize error */
424*32f884e0Stalley 		*error = -1;
425*32f884e0Stalley 
426*32f884e0Stalley 		if (slices != NULL) {
427*32f884e0Stalley 			zjni_free_array((void **)slices,
428*32f884e0Stalley 			    (zjni_free_f)dmgt_free_slice);
429*32f884e0Stalley 			slices = NULL;
430*32f884e0Stalley 		}
431*32f884e0Stalley 	}
432*32f884e0Stalley 
433fa9e4066Sahrens 	return (slices);
434fa9e4066Sahrens }
435fa9e4066Sahrens 
436fa9e4066Sahrens static void
437fa9e4066Sahrens get_disk_size(dm_descriptor_t media, char *name, uint64_t *size,
438fa9e4066Sahrens     uint32_t *blocksize, int *error)
439fa9e4066Sahrens {
440fa9e4066Sahrens 	nvlist_t *attrs;
441fa9e4066Sahrens 
442fa9e4066Sahrens 	*size = 0;
443fa9e4066Sahrens 	*error = 0;
444fa9e4066Sahrens 
445fa9e4066Sahrens 	attrs = dm_get_attributes(media, error);
446fa9e4066Sahrens 
447fa9e4066Sahrens 	if (*error) {
448fa9e4066Sahrens 		handle_error("could not get media attributes from disk: %s",
449fa9e4066Sahrens 		    name);
450fa9e4066Sahrens 	} else {
451fa9e4066Sahrens 		/* Try to get the number of accessible blocks */
452fa9e4066Sahrens 		nvpair_t *match = zjni_nvlist_walk_nvpair(
453fa9e4066Sahrens 		    attrs, DM_NACCESSIBLE, DATA_TYPE_UINT64, NULL);
454fa9e4066Sahrens 		if (match == NULL || nvpair_value_uint64(match, size)) {
455fa9e4066Sahrens 
456fa9e4066Sahrens 			/* Disk is probably not labeled, get raw size instead */
457fa9e4066Sahrens 			match = zjni_nvlist_walk_nvpair(
458fa9e4066Sahrens 			    attrs, DM_SIZE, DATA_TYPE_UINT64, NULL);
459fa9e4066Sahrens 			if (match == NULL || nvpair_value_uint64(match, size)) {
460fa9e4066Sahrens 				handle_error("could not get size of disk: %s",
461fa9e4066Sahrens 				    name);
462fa9e4066Sahrens 				*error = 1;
463fa9e4066Sahrens 			}
464fa9e4066Sahrens 		}
465fa9e4066Sahrens 
466fa9e4066Sahrens 		if (*error == 0) {
467fa9e4066Sahrens 			match = zjni_nvlist_walk_nvpair(
468fa9e4066Sahrens 			    attrs, DM_BLOCKSIZE, DATA_TYPE_UINT32, NULL);
469fa9e4066Sahrens 			if (match == NULL ||
470fa9e4066Sahrens 			    nvpair_value_uint32(match, blocksize)) {
471fa9e4066Sahrens 				handle_error("could not get "
472fa9e4066Sahrens 				    "block size of disk: %s", name);
473fa9e4066Sahrens 				*error = 1;
474fa9e4066Sahrens 			} else {
475fa9e4066Sahrens 				*size *= *blocksize;
476fa9e4066Sahrens 			}
477fa9e4066Sahrens 		}
478fa9e4066Sahrens 
479fa9e4066Sahrens 		nvlist_free(attrs);
480fa9e4066Sahrens 	}
481fa9e4066Sahrens }
482fa9e4066Sahrens 
483fa9e4066Sahrens static void
484fa9e4066Sahrens get_slice_use(dm_descriptor_t slice, char *name, char **used_name,
485fa9e4066Sahrens     char **used_by, int *error)
486fa9e4066Sahrens {
487fa9e4066Sahrens 	/* Get slice use statistics */
488fa9e4066Sahrens 	nvlist_t *stats = dm_get_stats(slice, DM_SLICE_STAT_USE, error);
489fa9e4066Sahrens 	if (*error != 0) {
490fa9e4066Sahrens 		handle_error("could not get stats of slice %s", name);
491fa9e4066Sahrens 	} else {
492fa9e4066Sahrens 
493fa9e4066Sahrens 		*used_name = NULL;
494fa9e4066Sahrens 		*used_by = NULL;
495fa9e4066Sahrens 
496fa9e4066Sahrens 		if (stats != NULL) {
497fa9e4066Sahrens 			char *tmp;
498fa9e4066Sahrens 			nvpair_t *match;
499fa9e4066Sahrens 
500fa9e4066Sahrens 			/* Get the type of usage for this slice */
501fa9e4066Sahrens 			match = zjni_nvlist_walk_nvpair(
502fa9e4066Sahrens 			    stats, DM_USED_BY, DATA_TYPE_STRING, NULL);
503fa9e4066Sahrens 
504fa9e4066Sahrens 			if (match != NULL &&
505fa9e4066Sahrens 			    nvpair_value_string(match, &tmp) == 0) {
506fa9e4066Sahrens 
507fa9e4066Sahrens 				*used_name = strdup(tmp);
508fa9e4066Sahrens 				if (*used_name == NULL) {
509fa9e4066Sahrens 					*error = -1;
510fa9e4066Sahrens 					handle_error("out of memory");
511fa9e4066Sahrens 				} else {
512fa9e4066Sahrens 
513fa9e4066Sahrens 					/* Get the object using this slice */
514fa9e4066Sahrens 					match =
515fa9e4066Sahrens 					    zjni_nvlist_walk_nvpair(stats,
516fa9e4066Sahrens 					    DM_USED_NAME, DATA_TYPE_STRING,
517fa9e4066Sahrens 					    NULL);
518fa9e4066Sahrens 
519fa9e4066Sahrens 					if (match != NULL &&
520fa9e4066Sahrens 					    nvpair_value_string(match, &tmp) ==
521fa9e4066Sahrens 					    0) {
522fa9e4066Sahrens 						*used_by = strdup(tmp);
523fa9e4066Sahrens 						if (*used_by == NULL) {
524fa9e4066Sahrens 							*error = -1;
525fa9e4066Sahrens 							handle_error(
526fa9e4066Sahrens 							    "out of memory");
527fa9e4066Sahrens 						}
528fa9e4066Sahrens 					}
529fa9e4066Sahrens 				}
530fa9e4066Sahrens 			}
531fa9e4066Sahrens 			nvlist_free(stats);
532fa9e4066Sahrens 		}
533fa9e4066Sahrens 	}
534fa9e4066Sahrens }
535fa9e4066Sahrens 
536fa9e4066Sahrens static dmgt_slice_t *
537fa9e4066Sahrens get_slice(dm_descriptor_t slice, uint32_t blocksize, int *error)
538fa9e4066Sahrens {
539fa9e4066Sahrens 	dmgt_slice_t *sp;
540fa9e4066Sahrens 	*error = 0;
541fa9e4066Sahrens 	sp = (dmgt_slice_t *)calloc(1, sizeof (dmgt_slice_t));
542fa9e4066Sahrens 	if (sp == NULL) {
543fa9e4066Sahrens 		*error = -1;
544fa9e4066Sahrens 		handle_error("out of memory");
545fa9e4066Sahrens 	} else {
546fa9e4066Sahrens 
547fa9e4066Sahrens 		/* Get name */
548fa9e4066Sahrens 		sp->name = get_device_name(slice, error);
549fa9e4066Sahrens 		if (!*error) {
550fa9e4066Sahrens 
551fa9e4066Sahrens 			nvlist_t *attrs = dm_get_attributes(slice, error);
552fa9e4066Sahrens 			if (*error) {
553fa9e4066Sahrens 				handle_error("could not get "
554fa9e4066Sahrens 				    "attributes from slice: %s", sp->name);
555fa9e4066Sahrens 			} else {
556fa9e4066Sahrens 				/* Get the size in blocks */
557fa9e4066Sahrens 				nvpair_t *match = zjni_nvlist_walk_nvpair(
558fa9e4066Sahrens 				    attrs, DM_SIZE, DATA_TYPE_UINT64, NULL);
559fa9e4066Sahrens 				uint64_t size_blocks;
560fa9e4066Sahrens 
561fa9e4066Sahrens 				sp->size = 0;
562fa9e4066Sahrens 
563fa9e4066Sahrens 				if (match == NULL ||
564fa9e4066Sahrens 				    nvpair_value_uint64(match, &size_blocks)) {
565fa9e4066Sahrens 					handle_error("could not get "
566fa9e4066Sahrens 					    "size of slice: %s", sp->name);
567fa9e4066Sahrens 					*error = 1;
568fa9e4066Sahrens 				} else {
569fa9e4066Sahrens 					uint64_t start_blocks;
570fa9e4066Sahrens 
571fa9e4066Sahrens 					/* Convert to bytes */
572fa9e4066Sahrens 					sp->size = blocksize * size_blocks;
573fa9e4066Sahrens 
574fa9e4066Sahrens 					/* Get the starting block */
575fa9e4066Sahrens 					match = zjni_nvlist_walk_nvpair(
576fa9e4066Sahrens 					    attrs, DM_START, DATA_TYPE_UINT64,
577fa9e4066Sahrens 					    NULL);
578fa9e4066Sahrens 
579fa9e4066Sahrens 					if (match == NULL ||
580fa9e4066Sahrens 					    nvpair_value_uint64(match,
581fa9e4066Sahrens 					    &start_blocks)) {
582fa9e4066Sahrens 						handle_error(
583fa9e4066Sahrens 						    "could not get "
584fa9e4066Sahrens 						    "start block of slice: %s",
585fa9e4066Sahrens 						    sp->name);
586fa9e4066Sahrens 						*error = 1;
587fa9e4066Sahrens 					} else {
588fa9e4066Sahrens 						/* Convert to bytes */
589fa9e4066Sahrens 						sp->start =
590fa9e4066Sahrens 						    blocksize * start_blocks;
591fa9e4066Sahrens 
592fa9e4066Sahrens 						/* Set slice use */
593fa9e4066Sahrens 						get_slice_use(slice, sp->name,
594fa9e4066Sahrens 						    &(sp->used_name),
595fa9e4066Sahrens 						    &(sp->used_by), error);
596fa9e4066Sahrens 					}
597fa9e4066Sahrens 				}
598fa9e4066Sahrens 			}
599fa9e4066Sahrens 		}
600fa9e4066Sahrens 	}
601fa9e4066Sahrens 
602fa9e4066Sahrens 	if (*error && sp != NULL) {
603fa9e4066Sahrens 		dmgt_free_slice(sp);
604fa9e4066Sahrens 	}
605fa9e4066Sahrens 
606fa9e4066Sahrens 	return (sp);
607fa9e4066Sahrens }
608fa9e4066Sahrens 
609fa9e4066Sahrens static void
610fa9e4066Sahrens handle_error(const char *format, ...)
611fa9e4066Sahrens {
612fa9e4066Sahrens 	va_list ap;
613fa9e4066Sahrens 	va_start(ap, format);
614fa9e4066Sahrens 
615fa9e4066Sahrens 	if (error_func != NULL) {
616fa9e4066Sahrens 		error_func(format, ap);
617fa9e4066Sahrens 	}
618fa9e4066Sahrens 
619fa9e4066Sahrens 	va_end(ap);
620fa9e4066Sahrens }
621fa9e4066Sahrens 
622fa9e4066Sahrens /* Should go away once 6285992 is fixed */
623fa9e4066Sahrens static int
624fa9e4066Sahrens slice_too_small(dmgt_slice_t *slice)
625fa9e4066Sahrens {
626fa9e4066Sahrens 	/* Check size */
627fa9e4066Sahrens 	if (slice->size < SPA_MINDEVSIZE) {
628fa9e4066Sahrens #ifdef DEBUG
629fa9e4066Sahrens 		(void) fprintf(stderr, "can't use %s: slice too small: %llu\n",
630fa9e4066Sahrens 			slice->name, (unsigned long long)slice->size);
631fa9e4066Sahrens #endif
632fa9e4066Sahrens 		return (1);
633fa9e4066Sahrens 	}
634fa9e4066Sahrens 
635fa9e4066Sahrens 	return (0);
636fa9e4066Sahrens }
637fa9e4066Sahrens 
638fa9e4066Sahrens static int
639*32f884e0Stalley slice_in_use(dmgt_slice_t *slice, int *error)
640fa9e4066Sahrens {
641*32f884e0Stalley 	char *msg = NULL;
642*32f884e0Stalley 	int in_use;
643fa9e4066Sahrens 
644*32f884e0Stalley 	/* Determine whether this slice could be passed to "zpool -f" */
645*32f884e0Stalley 	in_use = dm_inuse(slice->name, &msg, DM_WHO_ZPOOL_FORCE, error);
646*32f884e0Stalley 	if (*error) {
647*32f884e0Stalley 		handle_error("%s: could not determine usage", slice->name);
648fa9e4066Sahrens 	}
649fa9e4066Sahrens 
650fa9e4066Sahrens #ifdef DEBUG
651fa9e4066Sahrens 	if (in_use) {
652fa9e4066Sahrens 		(void) fprintf(stderr,
653*32f884e0Stalley 		    "can't use %s: used name: %s: used by: %s\n  message: %s\n",
654*32f884e0Stalley 		    slice->name, slice->used_name, slice->used_by, msg);
655fa9e4066Sahrens 	}
656fa9e4066Sahrens #endif
657fa9e4066Sahrens 
658*32f884e0Stalley 	if (msg != NULL) {
659*32f884e0Stalley 		free(msg);
660*32f884e0Stalley 	}
661*32f884e0Stalley 
662fa9e4066Sahrens 	return (in_use);
663fa9e4066Sahrens }
664fa9e4066Sahrens 
665fa9e4066Sahrens /*
666fa9e4066Sahrens  * Extern functions
667fa9e4066Sahrens  */
668fa9e4066Sahrens 
669fa9e4066Sahrens /*
670fa9e4066Sahrens  * Iterates through each available disk on the system.  For each free
671fa9e4066Sahrens  * dmgt_disk_t *, runs the given function with the dmgt_disk_t * as
672fa9e4066Sahrens  * the first arg and the given void * as the second arg.
673fa9e4066Sahrens  */
674fa9e4066Sahrens int
675fa9e4066Sahrens dmgt_avail_disk_iter(dmgt_disk_iter_f func, void *data)
676fa9e4066Sahrens {
677fa9e4066Sahrens 	int error = 0;
678fa9e4066Sahrens 	int filter[] = { DM_DT_FIXED, -1 };
679fa9e4066Sahrens 
680fa9e4066Sahrens 	/* Search for fixed disks */
681fa9e4066Sahrens 	dm_descriptor_t *disks = dm_get_descriptors(DM_DRIVE, filter, &error);
682fa9e4066Sahrens 
683fa9e4066Sahrens 	if (error) {
684fa9e4066Sahrens 		handle_error("unable to communicate with libdiskmgt");
685fa9e4066Sahrens 	} else {
686fa9e4066Sahrens 		int i;
687fa9e4066Sahrens 
688fa9e4066Sahrens 		/* For each disk... */
689*32f884e0Stalley 		for (i = 0; disks != NULL && disks[i] != NULL; i++) {
690fa9e4066Sahrens 			dm_descriptor_t disk = (dm_descriptor_t)disks[i];
691*32f884e0Stalley 			int online;
692*32f884e0Stalley 
693*32f884e0Stalley 			/* Reset error flag for each disk */
694*32f884e0Stalley 			error = 0;
695*32f884e0Stalley 
696*32f884e0Stalley 			/* Is this disk online? */
697*32f884e0Stalley 			online = get_disk_online(disk, &error);
698fa9e4066Sahrens 			if (!error && online) {
699*32f884e0Stalley 
700*32f884e0Stalley 				/* Get a dmgt_disk_t for this dm_descriptor_t */
701fa9e4066Sahrens 				dmgt_disk_t *dp = get_disk(disk, &error);
702dc307942Stalley 				if (!error) {
703*32f884e0Stalley 
704dc307942Stalley 					/*
705dc307942Stalley 					 * If this disk or any of its
706dc307942Stalley 					 * slices is usable...
707dc307942Stalley 					 */
708dc307942Stalley 					if (!dp->in_use ||
709dc307942Stalley 					    zjni_count_elements(
710dc307942Stalley 					    (void **)dp->slices) != 0) {
711dc307942Stalley 
712fa9e4066Sahrens 						/* Run the given function */
713fa9e4066Sahrens 						if (func(dp, data)) {
714fa9e4066Sahrens 							error = -1;
715fa9e4066Sahrens 						}
716fa9e4066Sahrens 						dmgt_free_disk(dp);
717dc307942Stalley #ifdef DEBUG
718dc307942Stalley 					} else {
719dc307942Stalley 						(void) fprintf(stderr, "disk "
720dc307942Stalley 						    "has no available slices: "
721dc307942Stalley 						    "%s\n", dp->name);
722dc307942Stalley #endif
723fa9e4066Sahrens 					}
724dc307942Stalley 
725dc307942Stalley 				}
726fa9e4066Sahrens 			}
727fa9e4066Sahrens 		}
728fa9e4066Sahrens 		dm_free_descriptors(disks);
729fa9e4066Sahrens 	}
730fa9e4066Sahrens 	return (error);
731fa9e4066Sahrens }
732fa9e4066Sahrens 
733fa9e4066Sahrens void
734fa9e4066Sahrens dmgt_free_disk(dmgt_disk_t *disk)
735fa9e4066Sahrens {
736fa9e4066Sahrens 	if (disk != NULL) {
737fa9e4066Sahrens 		free(disk->name);
738c8e9ed14Stalley 		zjni_free_array((void **)disk->aliases, free);
739c8e9ed14Stalley 		zjni_free_array((void **)disk->slices,
740c8e9ed14Stalley 		    (zjni_free_f)dmgt_free_slice);
741fa9e4066Sahrens 		free(disk);
742fa9e4066Sahrens 	}
743fa9e4066Sahrens }
744fa9e4066Sahrens 
745fa9e4066Sahrens void
746fa9e4066Sahrens dmgt_free_slice(dmgt_slice_t *slice)
747fa9e4066Sahrens {
748fa9e4066Sahrens 	if (slice != NULL) {
749fa9e4066Sahrens 		free(slice->name);
750fa9e4066Sahrens 		free(slice->used_name);
751fa9e4066Sahrens 		free(slice->used_by);
752fa9e4066Sahrens 		free(slice);
753fa9e4066Sahrens 	}
754fa9e4066Sahrens }
755fa9e4066Sahrens 
756fa9e4066Sahrens /*
757fa9e4066Sahrens  * For clients that need to capture error output.
758fa9e4066Sahrens  */
759fa9e4066Sahrens void
760fa9e4066Sahrens dmgt_set_error_handler(void (*func)(const char *, va_list))
761fa9e4066Sahrens {
762fa9e4066Sahrens 	error_func = func;
763fa9e4066Sahrens }
764