xref: /titanic_41/usr/src/lib/lvm/libmeta/common/meta_statconcise.c (revision d7cd82522afdd890a66c7600b499590ad44e84bd)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <meta.h>
29 #include <assert.h>
30 #include <ctype.h>
31 #include <mdiox.h>
32 #include <meta.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <strings.h>
36 #include <sys/lvm/md_mddb.h>
37 #include <sys/lvm/md_names.h>
38 #include <sys/lvm/md_crc.h>
39 #include <sys/lvm/md_convert.h>
40 
41 
42 /*
43  * Design Notes:
44  *
45  * All of the code in this file supports the addition of metastat -c output
46  * for the verbose option of metaimport.  Some of this code is also used by
47  * the command metastat for concise output(cmd/lvm/util/metastat.c).
48  * The code is designed to produce the same output as metastat -c does for a
49  * given diskset--with a couple exceptions.
50  * The primary differences between the output for the metastat -c command and
51  * metastat output for metaimport -v are:
52  *  - the set name is not printed next to each metadevice
53  *  - top-level state information is not printed for some metadevices
54  *  - the percent that a disk has completed resyncing is not listed
55  * in metaimport -v.
56  *
57  *
58  * The general layout of this file is as follows:
59  *
60  *  - report_metastat_info()
61  *	This is the primary entry point for the functions in this file, with
62  *	the exception of several functions that are also called from
63  *	cmd/io/lvm/util/metastat.c
64  *	report_metastat_info() calls functions to read in all the the
65  *	Directory blocks and Record blocks and then process the information
66  *	needed to print out the metadevice records in the same format as
67  *	metastat -c.
68  *
69  *  - read_all_mdrecords()
70  *	Reads in all the Directory blocks in the diskset and verifies their
71  *	validity.  For each Directly block, it loops through all Directory
72  *	Entries and for each one that contains a metadevice record calls
73  *	read_md_record().  Because the output is designed to imitate the
74  *	output of metastat -c, we ignore metadevice records for
75  *	optimized resync, changelog, and translog.
76  *
77  *  - read_md_record()
78  *	Reads in a Directory Entry and its associated Record block.  The
79  *	revision information for the Record block is checked and it is
80  *	determined whether or not it is a 64bit Record block or a 32bit record
81  *	block.  For each valid Record block, it allocates an md_im_rec_t
82  *	structure and calls extract_mduser_data().
83  *
84  *  - extract_mduser_data()
85  *	Populates the md_im_rec_t data structure with information about the
86  *	record's associated metadevice.  Also, the name of the metadevice is
87  *	either copied from the NM namespace(if it exists there) or is generated
88  *	from the record's un_self_id.
89  *
90  *  - process_toplevel_devices()
91  *	For a given metadevice type, searchs through the md_im_rec_t **mdimpp,
92  *	list of all metadevices in the set, to find all records of the
93  *	specified type that do not have a parent and puts them on a temp list.
94  *	The temp list is then iterated through and the associated processing
95  *	function is called.
96  *
97  *  - process_(trans, hotspare, hotspare_pool, soft_part, mirror, stripe, raid)
98  *	These functions are called by using the dfunc field in the mdimpp list.
99  *	Each process function only understands its own type of metadevice. Once
100  *	it processes the metadevice it was called for, it then loops through
101  *	all of the underlying metadevices.  After printing the name of the
102  *	underlying metadevice, it puts in on a list to be processed.  If the
103  *	underlying device is a physical device, then print_physical_device is
104  *	called.
105  *	Once all information about the original metadevice is processed, it
106  *	loops through the list of underlying metadevices and calls the
107  *	appropriate function to process them.
108  *
109  *  - process_toplevel_softparts()
110  *	To match the output for metastat -c, all top-level softpartions
111  *	are printed out in groups based on their underlying metadevice--so that
112  *	the underlying metadevice only needs to be processed once.
113  *
114  *  - meta_get_(sm_state, raid_col_state, stripe_state, hs_state)
115  *	These functions are used to retrieve the metadevice state information.
116  *	They are also used by the metastat concise routines in
117  *	cmd/lvm/util/metastat.c.
118  *
119  */
120 
121 
122 /*
123  * md_im_rec is a doubly linked list used to store the rb_data for each
124  * directory entry that corresponds to a metadevice.
125  * n_key: is set, if there is an associated entry in the NM namespace.
126  * dfunc: is set to point to the function that processes the particular
127  * metadevice associated with the record.
128  * hs_record_id: is only set, if the metadevice is a hotspare.
129  * un_self_id: is set for all other records. This is also used to generate
130  * the name of the metadevice if there is no entry for the metadevice in
131  * the NM namespace--n_key is not set.
132  */
133 typedef struct md_im_rec {
134 	mdkey_t			n_key; /* NM namespace key */
135 	struct md_im_rec 	*next;
136 	struct md_im_rec 	*prev;
137 	uint_t			md_type;
138 	uint_t			has_parent; /* either 0(no parent) or 1 */
139 	minor_t			un_self_id;
140 	mddb_recid_t		hs_record_id; /* hotspare recid */
141 	char 			*n_name;  /* name of metadevice */
142 	void 			(*dfunc) ();
143 	ushort_t		record_len;
144 	/* pointer to the unit structure for the metadevice, e.g. rb_data[0] */
145 	void			*record;
146 } md_im_rec_t;
147 
148 /*
149  * md_im_list is used to group toplevel metadevices by type and to group
150  * the underlying devices for a particular metadevice.
151  */
152 typedef struct md_im_list {
153 	struct md_im_list	*next;
154 	struct md_im_rec 	*mdrec;
155 } md_im_list_t;
156 
157 
158 /*
159  * MAXSIZEMDRECNAME is the value that has historically been used to allocate
160  * space for the metadevice name
161  */
162 #define	MAXSIZEMDRECNAME	20
163 #define	NAMEWIDTH		16
164 #define	offsetof(s, m)	((size_t)(&(((s *)0)->m)))
165 #define	NOT_PHYSICAL_DEV	0
166 #define	PHYSICAL_DEV		1
167 
168 
169 /*
170  * strip_blacks()
171  *
172  * Strip blanks from string.  Used for size field in concise output.
173  */
174 static char *
strip_blanks(char * s)175 strip_blanks(char *s)
176 {
177 	char *p;
178 
179 	for (p = s; *p; ) {
180 		if (*p == ' ') {
181 			char *t;
182 			for (t = p; *t; t++) {
183 				*t = *(t + 1);
184 			}
185 		} else {
186 			p++;
187 		}
188 	}
189 
190 	return (s);
191 }
192 
193 
194 /*
195  * print_concise_entry()
196  *
197  * Print properly indented metadevice name, type and size for concise output.
198  * This function is also called from: cmd/lvm/util/metastat.c.
199  */
200 void
print_concise_entry(int indent,char * name,diskaddr_t size,char mtype)201 print_concise_entry(int indent, char *name, diskaddr_t size, char mtype)
202 {
203 	int	i;
204 	int	width = NAMEWIDTH;	/* minumum field width for name */
205 	char	in[MAXPATHLEN];
206 	char	*sz;
207 
208 	in[0] = 0;
209 	for (i = 0; i < indent; i++)
210 		(void) strlcat(in, " ", sizeof (in));
211 
212 	/* set up minimum field width. negative for left justified */
213 	width -= indent;
214 	if (width < 0)
215 		width = 0;	/* overflowed; no minimum field needed */
216 	else
217 		width = 0 - width; /* negative for left justification */
218 
219 	if (size == 0) {
220 		sz = "-";
221 	} else {
222 		sz = strip_blanks(meta_number_to_string(size, DEV_BSIZE));
223 	}
224 
225 	(void) printf("%s%*s %c %6s", in, width, name, mtype, sz);
226 }
227 
228 
229 /*
230  * free_mdrec_list_entry()
231  *
232  * Removing entry from the list of metadevices in the diskset(mdimpp).
233  * This function will not remove the dummy entry at the head of the
234  * list, so we don't have to set mdrec equal to NULL.
235  */
236 static void
free_mdrec_list_entry(md_im_rec_t ** mdrec)237 free_mdrec_list_entry(md_im_rec_t  **mdrec)
238 {
239 	(*mdrec)->prev->next = (*mdrec)->next;
240 	if ((*mdrec)->next != NULL) {
241 		(*mdrec)->next->prev = (*mdrec)->prev;
242 	}
243 	Free((*mdrec)->record);
244 	Free((*mdrec)->n_name);
245 	Free(*mdrec);
246 }
247 
248 
249 /*
250  * ucomponent_append()
251  *
252  * Appending entry to the underlying component list.  The list
253  * is used to group all of the underlying devices before
254  * processing them.
255  */
256 static void
ucomponent_append(md_im_list_t ** ucomp_head,md_im_list_t ** ucomp_tail,md_im_list_t * ucomp)257 ucomponent_append(
258 	md_im_list_t	**ucomp_head,
259 	md_im_list_t	**ucomp_tail,
260 	md_im_list_t	*ucomp
261 )
262 {
263 	ucomp->next = NULL;
264 	if (*ucomp_head == NULL) {
265 		*ucomp_head = ucomp;
266 		*ucomp_tail = ucomp;
267 	} else {
268 		(*ucomp_tail)->next = ucomp;
269 		*ucomp_tail = (*ucomp_tail)->next;
270 	}
271 }
272 
273 
274 /*
275  * free_md_im_list_entries()
276  *
277  * Freeing entries on an md_im_list_t.  This list is used to group
278  * underlying components for processing and to group top-level metadevices
279  * by type.
280  */
281 static void
free_md_im_list_entries(md_im_list_t ** list_head)282 free_md_im_list_entries(md_im_list_t **list_head)
283 {
284 	md_im_list_t	*tmp_list_entry = *list_head;
285 	md_im_list_t	*rm_list_entry;
286 
287 	while (tmp_list_entry != NULL) {
288 		rm_list_entry = tmp_list_entry;
289 		tmp_list_entry = tmp_list_entry->next;
290 		Free(rm_list_entry);
291 	}
292 }
293 
294 
295 /*
296  * print_physical_device()
297  *
298  * If a metadevice has an underlying component that is a physical
299  * device, then this searches the pnm_rec_t list to match an entry's
300  * n_key to the key for the underlying component.  The ctd name of the
301  * physical device is printed on the same line as the metadevice.
302  */
303 static void
print_physical_device(pnm_rec_t * phys_nm,mdkey_t key)304 print_physical_device(
305 	pnm_rec_t	*phys_nm,
306 	mdkey_t		key
307 )
308 {
309 	pnm_rec_t	*tmpphys_nm;
310 
311 	for (tmpphys_nm = phys_nm; tmpphys_nm != NULL;
312 	    tmpphys_nm = tmpphys_nm->next) {
313 		if (tmpphys_nm->n_key == key) {
314 			(void) printf(" %s", tmpphys_nm->n_name);
315 			break;
316 		}
317 	}
318 }
319 
320 
321 /*
322  * get_stripe_req_size()
323  *
324  * Given a 64bit stripe unit, compute the size of the stripe unit.
325  * This function is a derivation of:
326  *	common/lvm/md_convert.c:get_big_stripe_req_size()
327  * and any changes made to either this function or get_big_stripe_req_size()
328  * should be reviewed to make sure the functionality in both places is correct.
329  *
330  * Returns:
331  *	total size of the 64bit stripe
332  */
333 size_t
get_stripe_req_size(ms_unit_t * un)334 get_stripe_req_size(ms_unit_t *un)
335 {
336 	struct ms_row *mdr;
337 	uint_t row;
338 	uint_t ncomps = 0;
339 	size_t mdsize = 0;
340 	size_t first_comp = 0;
341 
342 
343 	/* Compute the offset of the first component */
344 	first_comp = sizeof (ms_unit_t) +
345 	    sizeof (struct ms_row) * (un->un_nrows - 1);
346 	first_comp = roundup(first_comp, sizeof (long long));
347 
348 	/*
349 	 * Requestor wants to have the total size, add the sizes of
350 	 * all components
351 	 */
352 	mdr = &un->un_row[0];
353 	for (row = 0; (row < un->un_nrows); row++)
354 	    ncomps += mdr[row].un_ncomp;
355 	mdsize = first_comp + sizeof (ms_comp_t) * ncomps;
356 	return (mdsize);
357 }
358 
359 
360 /*
361  * meta_get_sm_state()
362  *
363  * Gets the state for the underlying components(submirrors) of a mirror.
364  * This function is also called from: cmd/lvm/util/metastat.c.
365  *
366  * Returns:
367  *	string for state of the sub-mirror
368  */
369 static char *
meta_get_sm_state(sm_state_t state)370 meta_get_sm_state(
371 	sm_state_t	state
372 )
373 {
374 	/* all is well */
375 	if (state & SMS_RUNNING) {
376 		return (NULL);
377 	}
378 
379 	/* resyncing, needs repair */
380 	if ((state & (SMS_COMP_RESYNC | SMS_ATTACHED_RESYNC |
381 	    SMS_OFFLINE_RESYNC))) {
382 		return (gettext("resyncing"));
383 	}
384 
385 	/* needs repair */
386 	if (state & (SMS_COMP_ERRED | SMS_ATTACHED | SMS_OFFLINE))
387 		return (gettext("maint"));
388 
389 	/* unknown */
390 	return (gettext("unknown"));
391 }
392 
393 
394 /*
395  * meta_get_raid_col_state()
396  *
397  * Gets the state for the underlying components(columns) of a raid.
398  * This function is also called from: cmd/lvm/util/metastat.c.
399  *
400  * Returns:
401  *	string for state of the raid column
402  *
403  */
404 char *
meta_get_raid_col_state(rcs_state_t state)405 meta_get_raid_col_state(
406 	rcs_state_t	state
407 )
408 {
409 	switch (state) {
410 		case RCS_INIT:
411 			return (gettext("initializing"));
412 		case RCS_OKAY:
413 			return (NULL);
414 		case RCS_INIT_ERRED:
415 			/*FALLTHROUGH*/
416 		case RCS_ERRED:
417 			return (gettext("maint"));
418 		case RCS_LAST_ERRED:
419 			return (gettext("last-erred"));
420 		case RCS_RESYNC:
421 			return (gettext("resyncing"));
422 		default:
423 			return (gettext("unknown"));
424 	}
425 }
426 
427 
428 /*
429  * meta_get_stripe_state()
430  *
431  * Gets the state for the underlying components of a stripe.
432  * This function is also called from: cmd/lvm/util/metastat.c.
433  *
434  * Returns:
435  *	string for state of the stripe
436  *
437  */
438 char *
meta_get_stripe_state(comp_state_t state)439 meta_get_stripe_state(
440 	comp_state_t	state
441 )
442 {
443 	switch (state) {
444 		case CS_OKAY:
445 			return (NULL);
446 		case CS_ERRED:
447 			return (gettext("maint"));
448 		case CS_LAST_ERRED:
449 			return (gettext("last-erred"));
450 		case CS_RESYNC:
451 			return (gettext("resyncing"));
452 		default:
453 			return (gettext("invalid"));
454 	}
455 }
456 
457 
458 /*
459  * meta_get_hs_state()
460  *
461  * Gets the state for the underlying components(hotspares) of a hotspare pool.
462  * This function is also called from: cmd/lvm/util/metastat.c.
463  *
464  * Returns:
465  *	string for state of the hotspare
466  *
467  */
468 char *
meta_get_hs_state(hotspare_states_t state)469 meta_get_hs_state(
470 	hotspare_states_t	state
471 )
472 {
473 	switch (state) {
474 		case HSS_AVAILABLE:
475 			return (NULL);
476 		case HSS_RESERVED:
477 			return (gettext("in-use"));
478 		case HSS_BROKEN:
479 			return (gettext("broken"));
480 		case HSS_UNUSED:
481 			/* FALLTHROUGH */
482 		default:
483 			return (gettext("invalid"));
484 	}
485 }
486 
487 
488 /*
489  * process_trans()
490  *
491  * Prints unit information for a trans metadevice and calls the respective
492  * functions to process the underlying metadevices.
493  *
494  */
495 static void
process_trans(md_im_rec_t ** mdimpp,int indent,pnm_rec_t * phys_nm,md_im_rec_t * mdrec)496 process_trans(
497 	md_im_rec_t	**mdimpp,
498 	int		indent,
499 	pnm_rec_t	*phys_nm,
500 	md_im_rec_t	*mdrec
501 )
502 {
503 	mt_unit_t	*mt;
504 	mdc_unit_t	uc;
505 	md_im_rec_t	*tmpmdrec;
506 	int		underlying_device = PHYSICAL_DEV;
507 
508 	mt = (mt_unit_t *)mdrec->record;
509 	uc = mt->c;
510 
511 	/* Printing name, size, and type of metadevice */
512 	print_concise_entry(indent, mdrec->n_name,
513 	    uc.un_total_blocks, 't');
514 
515 	/*
516 	 * Loops through md_im_rec_t **mdimpp list of all metadevices to find
517 	 * record that matches the underlying device.
518 	 * Trans devices can only have one underlying device, so once a
519 	 * match is found, we are done.
520 	 */
521 	for (tmpmdrec = *mdimpp; tmpmdrec != NULL;
522 	    tmpmdrec = tmpmdrec->next) {
523 		if (tmpmdrec->n_key == mt->un_m_key) {
524 			/* Printing name of the underlying metadevice */
525 			(void) printf(" %s", tmpmdrec->n_name);
526 			underlying_device = NOT_PHYSICAL_DEV;
527 			break;
528 		}
529 	}
530 
531 	/*
532 	 * If a metadevice was not found, then the underlying device must be a
533 	 * physical device.  Otherwise, call the functions to process the
534 	 * underlying devices.
535 	 */
536 	if (underlying_device == PHYSICAL_DEV) {
537 		print_physical_device(phys_nm, mt->un_m_key);
538 		(void) printf("\n");
539 	} else {
540 		/* process underlying component */
541 		(void) printf("\n");
542 		indent += META_INDENT;
543 		tmpmdrec->dfunc(mdimpp, indent, phys_nm, tmpmdrec);
544 	}
545 
546 	/*
547 	 * Removing the md_entry from the list
548 	 * of all metadevices
549 	 */
550 	free_mdrec_list_entry(&mdrec);
551 }
552 
553 
554 /*
555  * process_hotspare()
556  *
557  * Searches though list of physical devices to match hotspare record.
558  * Prints physical device name and state of a hotspare unit.
559  *
560  */
561 /*ARGSUSED*/
562 static void
process_hotspare(md_im_rec_t ** mdimpp,int indent,pnm_rec_t * phys_nm,md_im_rec_t * mdrec)563 process_hotspare(
564 	md_im_rec_t	**mdimpp,
565 	int		indent,
566 	pnm_rec_t	*phys_nm,
567 	md_im_rec_t	*mdrec
568 )
569 {
570 	hot_spare_t	*hs;
571 	pnm_rec_t	*tmpphys_nm;
572 	char 		*state = NULL;
573 
574 	hs =  (hot_spare_t *)mdrec->record;
575 
576 	/*
577 	 * Loops through physical namespace to find the device that matches
578 	 * the hotspare entry.
579 	 */
580 	for (tmpphys_nm = phys_nm; tmpphys_nm != NULL;
581 	    tmpphys_nm = tmpphys_nm->next) {
582 		if (tmpphys_nm->n_key ==
583 		    ((hot_spare_t *)hs)->hs_key) {
584 			/* Printing name of hotspare device */
585 			(void) printf(" %s", tmpphys_nm->n_name);
586 			break;
587 		}
588 	}
589 
590 	state = meta_get_hs_state(hs->hs_state);
591 	if (state != NULL)
592 		(void) printf(" (%s)", state);
593 
594 	/* Not removing entry, because it can be processed more than once. */
595 }
596 
597 
598 /*
599  * process_hotspare_pool()
600  *
601  * Prints concise unit information for a hotspare pool metadevice and calls a
602  * function to process each attached hotspare device.
603  *
604  */
605 static void
process_hotspare_pool(md_im_rec_t ** mdimpp,int indent,pnm_rec_t * phys_nm,md_im_rec_t * mdrec)606 process_hotspare_pool(
607 	md_im_rec_t	**mdimpp,
608 	int		indent,
609 	pnm_rec_t	*phys_nm,
610 	md_im_rec_t	*mdrec
611 )
612 {
613 	hot_spare_pool_ond_t	*hsp;
614 	int			i;
615 	md_im_rec_t		*tmpmdrec;
616 
617 	hsp =  (hot_spare_pool_ond_t *)mdrec->record;
618 
619 	/*
620 	 * Printing name, size, and type of metadevice. Setting size field to
621 	 * 0, so that output is the as metastat -c.
622 	 */
623 	print_concise_entry(indent, mdrec->n_name,
624 	    0, 'h');
625 
626 	/* Looping through list of attached hotspare devices. */
627 	for (i = 0; i < hsp->hsp_nhotspares; i++) {
628 		/* Looking for the matching record for the hotspare device. */
629 		for (tmpmdrec = *mdimpp; tmpmdrec != NULL;
630 		    tmpmdrec = tmpmdrec->next) {
631 			if (tmpmdrec->hs_record_id == hsp->hsp_hotspares[i]) {
632 				/* Calling function to print name of hotspare */
633 				tmpmdrec->dfunc(mdimpp, indent, phys_nm,
634 				    tmpmdrec);
635 			}
636 		}
637 	}
638 	(void) printf("\n");
639 
640 	/*
641 	 * Removing the md_entry from the list
642 	 * of all metadevices
643 	 */
644 	free_mdrec_list_entry(&mdrec);
645 }
646 
647 
648 /*
649  * process_raid()
650  *
651  * Prints concise unit information for a raid metadevice and calls the
652  * respective functions to process the underlying metadevices.
653  *
654  */
655 static void
process_raid(md_im_rec_t ** mdimpp,int indent,pnm_rec_t * phys_nm,md_im_rec_t * mdrec)656 process_raid(
657 	md_im_rec_t	**mdimpp,
658 	int		indent,
659 	pnm_rec_t	*phys_nm,
660 	md_im_rec_t	*mdrec
661 )
662 {
663 	mr_unit_t	*mr;
664 	mr_column_t	*mc;
665 	mdc_unit_t	uc;
666 	int		i;
667 	md_im_rec_t	*tmpmdrec;
668 	md_im_rec_t	*hstmpmdrec;
669 	md_im_list_t	*ucomp_head = NULL;
670 	md_im_list_t	*ucomp_tail = NULL;
671 	md_im_list_t	*ucomp = NULL;
672 	pnm_rec_t	*tmpphys_nm;
673 	int		underlying_device;
674 
675 	mr =  (mr_unit_t *)mdrec->record;
676 	uc = mr->c;
677 
678 	/* Printing name, size, and type of metadevice */
679 	print_concise_entry(indent, mdrec->n_name,
680 	    uc.un_total_blocks, 'r');
681 
682 	/* Loops through raid columns to find underlying metadevices */
683 	for (i = 0, mc = &mr->un_column[0];  i < mr->un_totalcolumncnt;
684 	    i++, mc++) {
685 		char	*state = NULL;
686 		char	*hsname = NULL;
687 
688 		/*
689 		 * Need to assume that underlying device is a physical device,
690 		 * unless we find a matching metadevice record.
691 		 */
692 		underlying_device = PHYSICAL_DEV;
693 
694 		/*
695 		 * Loops through list of metadevices to find record that matches
696 		 * the underlying device.
697 		 */
698 		for (tmpmdrec = *mdimpp; tmpmdrec != NULL;
699 		    tmpmdrec = tmpmdrec->next) {
700 			if (tmpmdrec->n_key == mc->un_orig_key) {
701 				/* check if hotspare device enabled */
702 				if (mc->un_hs_id !=  NULL) {
703 					/*
704 					 * Find matching metadevice record
705 					 * for the hotspare device.
706 					 */
707 					for (hstmpmdrec = *mdimpp;
708 					    hstmpmdrec != NULL;
709 					    hstmpmdrec = hstmpmdrec->next) {
710 						if (hstmpmdrec->hs_record_id ==
711 						    mc->un_hs_id) {
712 							/* print name of hs */
713 							hstmpmdrec->dfunc(
714 							    mdimpp, indent,
715 							    phys_nm,
716 							    hstmpmdrec);
717 							break;
718 						}
719 					}
720 				}
721 				/* print name of underlying metadevice */
722 				(void) printf(" %s", tmpmdrec->n_name);
723 				underlying_device = NOT_PHYSICAL_DEV;
724 				ucomp = Zalloc(sizeof (md_im_list_t));
725 				ucomp->mdrec = tmpmdrec;
726 				ucomponent_append(&ucomp_head, &ucomp_tail,
727 				    ucomp);
728 			}
729 		}
730 
731 		if (underlying_device == PHYSICAL_DEV) {
732 			print_physical_device(phys_nm, mc->un_orig_key);
733 		}
734 		state = meta_get_raid_col_state(mc->un_devstate);
735 
736 		/*
737 		 * An underlying hotspare must be a physical device.
738 		 * If support is ever added for soft-partitions under
739 		 * hotspare pools, then this code should be updated to
740 		 * include a search for underlying metadevices.
741 		 */
742 		if (mc->un_hs_id != 0) {
743 			for (tmpphys_nm = phys_nm; tmpphys_nm != NULL;
744 			    tmpphys_nm = tmpphys_nm->next) {
745 				if (tmpphys_nm->n_key == mc->un_hs_key) {
746 					hsname = tmpphys_nm->n_name;
747 					break;
748 				}
749 			}
750 		}
751 
752 		if (state != NULL) {
753 			if (hsname != NULL)
754 				(void) printf(" (%s-%s)", state,
755 				    hsname);
756 			else
757 				(void) printf(" (%s)", state);
758 		} else if (hsname != NULL) {
759 			(void) printf(gettext(" (spared-%s)"), hsname);
760 		}
761 	}
762 	(void) printf("\n");
763 
764 	/* process underlying components */
765 	indent += META_INDENT;
766 	for (ucomp = ucomp_head; ucomp != NULL;
767 	    ucomp = ucomp->next) {
768 		ucomp->mdrec->dfunc(mdimpp, indent, phys_nm,
769 		    ucomp->mdrec);
770 	}
771 	free_md_im_list_entries(&ucomp_head);
772 
773 	/*
774 	 * Removing the md_entry from the list
775 	 * of all metadevices
776 	 */
777 	free_mdrec_list_entry(&mdrec);
778 }
779 
780 
781 /*
782  * process_mirror()
783  *
784  * Prints concise unit information for a mirror metadevice and calls the
785  * respective functions to process the underlying metadevices.
786  *
787  */
788 static void
process_mirror(md_im_rec_t ** mdimpp,int indent,pnm_rec_t * phys_nm,md_im_rec_t * mdrec)789 process_mirror(
790 	md_im_rec_t	**mdimpp,
791 	int		indent,
792 	pnm_rec_t	*phys_nm,
793 	md_im_rec_t	*mdrec
794 )
795 {
796 	mm_unit_t	*mm;
797 	mm_submirror_t 	*sm;
798 	mdc_unit_t	uc;
799 	int		i;
800 	md_im_rec_t	*tmpmdrec;
801 	md_im_list_t	*ucomp_head = NULL;
802 	md_im_list_t	*ucomp_tail = NULL;
803 	md_im_list_t	*ucomp = NULL;
804 
805 	mm =  (mm_unit_t *)mdrec->record;
806 	uc = mm->c;
807 
808 	/* Printing name, size, and type of metadevice */
809 	print_concise_entry(indent, mdrec->n_name,
810 	    uc.un_total_blocks, 'm');
811 
812 	/* Looping through sub-mirrors to find underlying devices */
813 	for (i = 0, sm = &mm->un_sm[0]; i < mm->un_nsm; i++, sm++) {
814 		char 	*state = NULL;
815 
816 		for (tmpmdrec = *mdimpp; tmpmdrec != NULL;
817 		    tmpmdrec = tmpmdrec->next) {
818 			if (tmpmdrec->n_key == sm->sm_key) {
819 				(void) printf(" %s", tmpmdrec->n_name);
820 				ucomp = Zalloc(sizeof (md_im_list_t));
821 				ucomp->mdrec = tmpmdrec;
822 				ucomponent_append(&ucomp_head, &ucomp_tail,
823 				    ucomp);
824 			}
825 		}
826 
827 		/*
828 		 * It is not possible to have an underlying physical device
829 		 * for a submirror, so there is no need to search the phys_nm
830 		 * list.
831 		 */
832 
833 		/* Printing the state for the submirror */
834 		state = meta_get_sm_state(sm->sm_state);
835 		if (state != NULL) {
836 			(void) printf(" (%s)", state);
837 		}
838 	}
839 	(void) printf("\n");
840 
841 	/* process underlying components */
842 	indent += META_INDENT;
843 	for (ucomp = ucomp_head; ucomp != NULL;
844 	    ucomp = ucomp->next) {
845 		ucomp->mdrec->dfunc(mdimpp, indent, phys_nm,
846 		    ucomp->mdrec);
847 	}
848 	free_md_im_list_entries(&ucomp_head);
849 
850 	/*
851 	 * Removing the md_entry from the list
852 	 * of all metadevices
853 	 */
854 	free_mdrec_list_entry(&mdrec);
855 }
856 
857 
858 /*
859  * process_stripe()
860  *
861  * Prints concise unit information for a stripe metadevice and calls the
862  * respective functions to process the underlying metadevices.
863  *
864  */
865 static void
process_stripe(md_im_rec_t ** mdimpp,int indent,pnm_rec_t * phys_nm,md_im_rec_t * mdrec)866 process_stripe(
867 	md_im_rec_t	**mdimpp,
868 	int		indent,
869 	pnm_rec_t	*phys_nm,
870 	md_im_rec_t	*mdrec
871 )
872 {
873 	ms_unit_t	*ms;
874 	mdc_unit_t	uc;
875 	md_im_rec_t	*tmpmdrec;
876 	md_im_list_t	*ucomp_head = NULL;
877 	md_im_list_t	*ucomp_tail = NULL;
878 	md_im_list_t	*ucomp = NULL;
879 	pnm_rec_t	*tmpphys_nm;
880 	int		underlying_device;
881 	uint_t		row;
882 
883 	ms =  (ms_unit_t *)mdrec->record;
884 	uc = ms->c;
885 
886 	/* Printing name, size, and type of metadevice */
887 	print_concise_entry(indent, mdrec->n_name,
888 	    uc.un_total_blocks, 's');
889 
890 	/* Looping through stripe rows */
891 	for (row = 0; (row < ms->un_nrows); ++row) {
892 		struct ms_row	*mdr = &ms->un_row[row];
893 		ms_comp_t	*mdcomp = (void *)&((char *)ms)
894 		    [ms->un_ocomp];
895 		uint_t		comp, c;
896 
897 		/*
898 		 * Looping through the components in each row to find the
899 		 * underlying devices.
900 		 */
901 		for (comp = 0, c = mdr->un_icomp; (comp < mdr->un_ncomp);
902 		    ++comp, ++c) {
903 			char		*state = NULL;
904 			char		*hsname = NULL;
905 			ms_comp_t	*mdc = &mdcomp[c];
906 			md_m_shared_t 	*mdm = &mdc->un_mirror;
907 
908 			/*
909 			 * Need to assume that underlying device is a
910 			 * physical device, unless we find a matching
911 			 * metadevice record.
912 			 */
913 			underlying_device = PHYSICAL_DEV;
914 
915 			for (tmpmdrec = *mdimpp; tmpmdrec != NULL;
916 			    tmpmdrec = tmpmdrec->next) {
917 				if (tmpmdrec->n_key == mdc->un_key) {
918 					(void) printf(" %s", tmpmdrec->n_name);
919 					underlying_device = NOT_PHYSICAL_DEV;
920 					ucomp = Zalloc(sizeof (md_im_list_t));
921 					ucomp->mdrec = tmpmdrec;
922 					ucomponent_append(&ucomp_head,
923 					    &ucomp_tail, ucomp);
924 				}
925 			}
926 			/* if an underlying metadevice was not found */
927 			if (underlying_device == PHYSICAL_DEV) {
928 				print_physical_device(phys_nm, mdc->un_key);
929 			}
930 			state = meta_get_stripe_state(mdm->ms_state);
931 
932 			/*
933 			 * An underlying hotspare must be a physical device.
934 			 * If support is ever added for soft-partitions under
935 			 * hotspare pools, then this code should be updated to
936 			 * include a search for underlying metadevices.
937 			 */
938 			if (mdm->ms_hs_key != 0) {
939 				for (tmpphys_nm = phys_nm; tmpphys_nm != NULL;
940 				    tmpphys_nm = tmpphys_nm->next) {
941 					if (tmpphys_nm->n_key ==
942 					    mdm->ms_hs_key) {
943 						hsname = tmpphys_nm->n_name;
944 						break;
945 					}
946 				}
947 			}
948 			if (state != NULL) {
949 				if (hsname != NULL) {
950 					(void) printf(" (%s-%s)", state,
951 					    hsname);
952 				} else {
953 					(void) printf(" (%s)", state);
954 				}
955 			} else if (hsname != NULL) {
956 				(void) printf(gettext(" (spared-%s)"), hsname);
957 			}
958 		}
959 	}
960 	(void) printf("\n");
961 
962 	/* Process underlying metadevices */
963 	indent += META_INDENT;
964 	for (ucomp = ucomp_head; ucomp != NULL;
965 	    ucomp = ucomp->next) {
966 		ucomp->mdrec->dfunc(mdimpp, indent, phys_nm,
967 		    ucomp->mdrec);
968 	}
969 	free_md_im_list_entries(&ucomp_head);
970 
971 	/*
972 	 * Removing the md_entry from the list
973 	 * of all metadevices
974 	 */
975 	free_mdrec_list_entry(&mdrec);
976 }
977 
978 
979 /*
980  * process_softpart()
981  *
982  * Prints concise unit information for a softpart metadevice and calls the
983  * respective functions to process the underlying metadevices.
984  *
985  */
986 static void
process_softpart(md_im_rec_t ** mdimpp,int indent,pnm_rec_t * phys_nm,md_im_rec_t * mdrec)987 process_softpart(
988 	md_im_rec_t	**mdimpp,
989 	int		indent,
990 	pnm_rec_t	*phys_nm,
991 	md_im_rec_t	*mdrec
992 )
993 {
994 	mp_unit_t	*mp;
995 	mdc_unit_t	uc;
996 	md_im_rec_t	*tmpmdrec;
997 	int		underlying_device = PHYSICAL_DEV;
998 
999 	mp =  (mp_unit_t *)mdrec->record;
1000 	uc = mp->c;
1001 
1002 	/* Printing name, size, and type of metadevice */
1003 	print_concise_entry(indent, mdrec->n_name,
1004 	    uc.un_total_blocks, 'p');
1005 
1006 	/*
1007 	 * Loops through md_im_rec_t **mdimpp list of all metadevices to find
1008 	 * record that matches the underlying device.
1009 	 * Softpartitions can only have one underlying device, so once a
1010 	 * match is found, we are done.
1011 	 */
1012 	for (tmpmdrec = *mdimpp; tmpmdrec != NULL;
1013 	    tmpmdrec = tmpmdrec->next) {
1014 		if (tmpmdrec->n_key == mp->un_key) {
1015 			/* Printing name of the underlying metadevice */
1016 			(void) printf(" %s", tmpmdrec->n_name);
1017 			underlying_device = NOT_PHYSICAL_DEV;
1018 			break;
1019 		}
1020 	}
1021 
1022 	/* This is only executed if an underlying metadevice was not found */
1023 	if (underlying_device == PHYSICAL_DEV) {
1024 		print_physical_device(phys_nm, mp->un_key);
1025 		(void) printf("\n");
1026 	} else {
1027 		/* Process underlying metadevice */
1028 		(void) printf("\n");
1029 		indent += META_INDENT;
1030 		tmpmdrec->dfunc(mdimpp, indent, phys_nm,
1031 		    tmpmdrec);
1032 	}
1033 
1034 	/*
1035 	 * Removing the md_entry from the list
1036 	 * of all metadevices
1037 	 */
1038 	free_mdrec_list_entry(&mdrec);
1039 }
1040 
1041 
1042 /*
1043  * process_toplevel_softparts()
1044  *
1045  * Toplevel softpartions need to be grouped so that their underlying devices
1046  * can be printed just once.
1047  */
1048 static void
process_toplevel_softparts(md_im_rec_t ** mdimpp,int indent,pnm_rec_t * phys_nm)1049 process_toplevel_softparts(
1050 	md_im_rec_t	**mdimpp,
1051 	int		indent,
1052 	pnm_rec_t	*phys_nm
1053 )
1054 {
1055 	mp_unit_t	*mp;
1056 	mdc_unit_t	uc;
1057 	md_im_rec_t	*mdrec;
1058 	md_im_rec_t	*comp_mdrec; /* pntr to underlying component's record */
1059 	md_im_rec_t	*tmp_mdrec, *rm_mdrec;
1060 	mp_unit_t	*tmp_mp;
1061 	int		underlying_device;
1062 
1063 	/*
1064 	 * Loops through md_im_rec_t **mdimpp list of all metadevices to find
1065 	 * all softpartions that are toplevel softpartitions(softparts w/out
1066 	 * a parent). Groups output for these entries so that the function to
1067 	 * process the underlying metadevice is only called once.
1068 	 */
1069 	for (mdrec = *mdimpp; mdrec != NULL; mdrec = mdrec->next) {
1070 
1071 		underlying_device = PHYSICAL_DEV;
1072 		if ((mdrec->md_type == MDDB_F_SOFTPART) &&
1073 		    (mdrec->has_parent == 0)) {
1074 			mp =  (mp_unit_t *)mdrec->record;
1075 			uc = mp->c;
1076 			/* Printing name, size, and type of metadevice */
1077 			print_concise_entry(indent, mdrec->n_name,
1078 			    uc.un_total_blocks, 'p');
1079 			/*
1080 			 * Looking for record that matches underlying
1081 			 * component.
1082 			 */
1083 			for (comp_mdrec = *mdimpp; comp_mdrec != NULL;
1084 			    comp_mdrec = comp_mdrec->next) {
1085 				if (comp_mdrec->n_key == mp->un_key) {
1086 					/* Print name of underlying device */
1087 					(void) printf(" %s",
1088 					    comp_mdrec->n_name);
1089 					underlying_device = NOT_PHYSICAL_DEV;
1090 					break;
1091 				}
1092 			}
1093 			if (underlying_device == PHYSICAL_DEV) {
1094 				print_physical_device(phys_nm, mp->un_key);
1095 			}
1096 			(void) printf("\n");
1097 
1098 			/*
1099 			 * Looking for any other toplevel softpartitions with
1100 			 * same underlying device. We know that all other
1101 			 * matching metadevices, that share the same underlying
1102 			 * metadevice, are also soft-partitions.
1103 			 */
1104 			for (tmp_mdrec = mdrec->next; tmp_mdrec != NULL; ) {
1105 				tmp_mp = (mp_unit_t *)tmp_mdrec->record;
1106 				if ((tmp_mdrec->has_parent == 0) &&
1107 				    (tmp_mp->un_key == mp->un_key)) {
1108 					uc = tmp_mp->c;
1109 					print_concise_entry(indent,
1110 					    tmp_mdrec->n_name,
1111 					    uc.un_total_blocks, 'p');
1112 					if (underlying_device ==
1113 					    NOT_PHYSICAL_DEV) {
1114 						(void) printf(" %s",
1115 						    comp_mdrec->n_name);
1116 					} else {
1117 						print_physical_device(
1118 						    phys_nm, tmp_mp->un_key);
1119 					}
1120 					(void) printf("\n");
1121 					/*
1122 					 * Need to advance so that will not lose
1123 					 * position after removing processed
1124 					 * record.
1125 					 */
1126 					rm_mdrec = tmp_mdrec;
1127 					tmp_mdrec = tmp_mdrec->next;
1128 					/*
1129 					 * Removing the md_entry from the list
1130 					 * of all metadevices.
1131 					 */
1132 					free_mdrec_list_entry(&rm_mdrec);
1133 				} else {
1134 					tmp_mdrec = tmp_mdrec->next;
1135 				}
1136 			}
1137 			/* Process the underlying device */
1138 			if (underlying_device == NOT_PHYSICAL_DEV) {
1139 				indent += META_INDENT;
1140 				comp_mdrec->dfunc(mdimpp, indent, phys_nm,
1141 				    comp_mdrec);
1142 			}
1143 		}
1144 	}
1145 }
1146 
1147 
1148 /*
1149  * process_toplevel_devices()
1150  *
1151  * Search through list of metadevices for metadevices of md_type that do not
1152  * have a parent.
1153  *
1154  */
1155 static void
process_toplevel_devices(md_im_rec_t ** mdimpp,pnm_rec_t * pnm,uint_t md_type)1156 process_toplevel_devices(
1157 	md_im_rec_t	**mdimpp,
1158 	pnm_rec_t	*pnm,
1159 	uint_t		md_type
1160 )
1161 {
1162 	md_im_rec_t	*mdrec;
1163 	md_im_list_t	*mdrec_tl_tail = NULL;
1164 	md_im_list_t	*mdrec_tl_head = NULL;
1165 	md_im_list_t	*tmp_tl_list = NULL;
1166 	int		indent = 0;
1167 
1168 	indent += META_INDENT;
1169 
1170 	/*
1171 	 * Need to group soft partitions so that common underlying device
1172 	 * are only processed once.
1173 	 */
1174 	if (md_type == MDDB_F_SOFTPART) {
1175 		process_toplevel_softparts(mdimpp, indent, pnm);
1176 		return;
1177 	}
1178 
1179 	/*
1180 	 * Search the list of metadevices to find all metadevices that match
1181 	 * the type and don't have a parent.  Put them on a separate list
1182 	 * that will be processed.
1183 	 */
1184 	for (mdrec = *mdimpp; mdrec != NULL; mdrec = mdrec->next) {
1185 		if ((mdrec->md_type == md_type)&&(mdrec->has_parent == 0)) {
1186 			tmp_tl_list = Zalloc(sizeof (md_im_list_t));
1187 			tmp_tl_list->mdrec = mdrec;
1188 			tmp_tl_list->next = NULL;
1189 			if (mdrec_tl_tail == NULL) {
1190 				mdrec_tl_tail = tmp_tl_list;
1191 				mdrec_tl_head = mdrec_tl_tail;
1192 			} else {
1193 				mdrec_tl_tail->next = tmp_tl_list;
1194 				mdrec_tl_tail = mdrec_tl_tail->next;
1195 			}
1196 		}
1197 
1198 	}
1199 
1200 	/*
1201 	 * Loop through list and process all top-level metadevices of a
1202 	 * given type.
1203 	 */
1204 	for (tmp_tl_list = mdrec_tl_head; tmp_tl_list != NULL;
1205 	    tmp_tl_list = tmp_tl_list->next) {
1206 		tmp_tl_list->mdrec->dfunc(mdimpp, indent, pnm,
1207 		    tmp_tl_list->mdrec);
1208 	}
1209 
1210 	free_md_im_list_entries(&mdrec_tl_head);
1211 }
1212 
1213 
1214 /*
1215  * extract_mduser_data()
1216  *
1217  * Converts or copies the (mddb_rb_t->rb_data) metadevice record to a 64bit
1218  * record.
1219  * Sets the dfunc field to point to the appropriate function to process the
1220  * metadevice.
1221  * Sets the parent field for the metadevice.
1222  * Extracts the name from the NM namespace if it is available, otherwise
1223  * generates it from the metadevice's minor number.
1224  *
1225  * Returns:
1226  *	< 0 for failure
1227  *	  0 for success
1228  *
1229  */
1230 static int
extract_mduser_data(mddb_rb_t * nm,md_im_rec_t * mdrec,void * rbp,int is_32bit_record,md_error_t * ep)1231 extract_mduser_data(
1232 	mddb_rb_t		*nm,
1233 	md_im_rec_t		*mdrec,
1234 	void			*rbp,
1235 	int 			is_32bit_record,
1236 	md_error_t		*ep
1237 )
1238 {
1239 	mdc_unit_t		uc;
1240 	hot_spare_t 		*hs;
1241 	hot_spare_pool_ond_t 	*hsp;
1242 	size_t			newreqsize;
1243 	mddb_rb_t		*rbp_nm = nm;
1244 	struct nm_rec		*nm_record;
1245 	struct nm_name		*nmname;
1246 	char 			*uname = NULL;
1247 
1248 
1249 	/*LINTED*/
1250 	nm_record = (struct nm_rec *)((caddr_t)(&rbp_nm->rb_data));
1251 
1252 	/*
1253 	 * Setting the un_self_id or the hs_self_id, in the case of hotspare
1254 	 * records, for each metadevice entry. Also setting has_parent and
1255 	 * setting dfunc so that it points to the correct function to process
1256 	 * the record type.
1257 	 * If the record was stored ondisk in 32bit format, then it is
1258 	 * converted to the 64bits equivalent 64bit format and the memory
1259 	 * for the 32bit pointer is freed.
1260 	 */
1261 	switch (mdrec->md_type) {
1262 		case MDDB_F_SOFTPART:
1263 			if (is_32bit_record) {
1264 				mp_unit32_od_t	*small_un;
1265 				mp_unit_t	*big_un;
1266 
1267 				small_un = (mp_unit32_od_t *)((uintptr_t)rbp +
1268 				    (sizeof (mddb_rb_t) - sizeof (int)));
1269 				newreqsize = sizeof (mp_unit_t) +
1270 				    ((small_un->un_numexts - 1) *
1271 				    sizeof (struct mp_ext));
1272 				big_un = (void *)Zalloc(newreqsize);
1273 				softpart_convert((caddr_t)small_un,
1274 				    (caddr_t)big_un, SMALL_2_BIG);
1275 				mdrec->record = (void *)big_un;
1276 			} else {
1277 				mp_unit_t	*big_un;
1278 
1279 				big_un = (mp_unit_t *)((uintptr_t)rbp +
1280 				    (sizeof (mddb_rb_t) - sizeof (int)));
1281 				newreqsize = sizeof (mp_unit_t) +
1282 				    ((big_un->un_numexts - 1) *
1283 				    sizeof (struct mp_ext));
1284 				mdrec->record = (void *)Zalloc(newreqsize);
1285 				bcopy(big_un, mdrec->record, newreqsize);
1286 			}
1287 			uc = ((mp_unit_t *)mdrec->record)->c;
1288 			mdrec->dfunc = &process_softpart;
1289 			mdrec->un_self_id = uc.un_self_id;
1290 			mdrec->has_parent = MD_HAS_PARENT(
1291 			    uc.un_parent);
1292 			break;
1293 		case MDDB_F_STRIPE:
1294 			if (is_32bit_record) {
1295 				ms_unit32_od_t	*small_un;
1296 				ms_unit_t	*big_un;
1297 
1298 				small_un = (ms_unit32_od_t *)((uintptr_t)rbp +
1299 				    (sizeof (mddb_rb_t) - sizeof (int)));
1300 				newreqsize = get_big_stripe_req_size(
1301 				    small_un, COMPLETE_STRUCTURE);
1302 				    big_un = (void *)Zalloc(newreqsize);
1303 				stripe_convert((caddr_t)small_un,
1304 				    (caddr_t)big_un, SMALL_2_BIG);
1305 				mdrec->record = (void *)big_un;
1306 			} else {
1307 				ms_unit_t	*big_un;
1308 
1309 				big_un = (ms_unit_t *)((uintptr_t)rbp +
1310 				    (sizeof (mddb_rb_t) - sizeof (int)));
1311 				newreqsize = get_stripe_req_size(big_un);
1312 				mdrec->record = (void *)Zalloc(newreqsize);
1313 				bcopy(big_un, mdrec->record, newreqsize);
1314 			}
1315 			uc = ((ms_unit_t *)mdrec->record)->c;
1316 			mdrec->dfunc = &process_stripe;
1317 			mdrec->un_self_id = uc.un_self_id;
1318 			mdrec->has_parent = MD_HAS_PARENT(
1319 			    uc.un_parent);
1320 			break;
1321 		case MDDB_F_MIRROR:
1322 			if (is_32bit_record) {
1323 				mm_unit32_od_t	*small_un;
1324 				mm_unit_t	*big_un;
1325 
1326 				small_un = (mm_unit32_od_t *)((uintptr_t)rbp +
1327 				    (sizeof (mddb_rb_t) - sizeof (int)));
1328 				newreqsize = sizeof (mm_unit_t);
1329 				big_un = (void *)Zalloc(newreqsize);
1330 				mirror_convert((caddr_t)small_un,
1331 				    (caddr_t)big_un, SMALL_2_BIG);
1332 				mdrec->record = (void *)big_un;
1333 			} else {
1334 				mm_unit_t	*big_un;
1335 
1336 				big_un = (mm_unit_t *)((uintptr_t)rbp +
1337 				    (sizeof (mddb_rb_t) - sizeof (int)));
1338 				newreqsize = sizeof (mm_unit_t);
1339 				mdrec->record = (void *)Zalloc(newreqsize);
1340 				bcopy(big_un, mdrec->record, newreqsize);
1341 			}
1342 			uc = ((mm_unit_t *)mdrec->record)->c;
1343 			mdrec->dfunc = &process_mirror;
1344 			mdrec->un_self_id = uc.un_self_id;
1345 			mdrec->has_parent = MD_HAS_PARENT(
1346 			    uc.un_parent);
1347 			break;
1348 		case MDDB_F_RAID:
1349 			if (is_32bit_record) {
1350 				mr_unit32_od_t	*small_un;
1351 				mr_unit_t	*big_un;
1352 				uint_t		ncol;
1353 
1354 				small_un = (mr_unit32_od_t *)((uintptr_t)rbp +
1355 				    (sizeof (mddb_rb_t) - sizeof (int)));
1356 				ncol = small_un->un_totalcolumncnt;
1357 				newreqsize = sizeof (mr_unit_t) +
1358 				    ((ncol - 1) * sizeof (mr_column_t));
1359 				big_un = (void *)Zalloc(newreqsize);
1360 				raid_convert((caddr_t)small_un,
1361 				    (caddr_t)big_un, SMALL_2_BIG);
1362 				mdrec->record = (void *)big_un;
1363 			} else {
1364 				mr_unit_t	*big_un;
1365 				uint_t		ncol;
1366 
1367 				big_un = (mr_unit_t *)((uintptr_t)rbp +
1368 				    (sizeof (mddb_rb_t) - sizeof (int)));
1369 				ncol = big_un->un_totalcolumncnt;
1370 				newreqsize = sizeof (mr_unit_t) +
1371 				    ((ncol - 1) * sizeof (mr_column_t));
1372 				mdrec->record = (void *)Zalloc(newreqsize);
1373 				bcopy(big_un, mdrec->record, newreqsize);
1374 			}
1375 			uc = ((mr_unit_t *)mdrec->record)->c;
1376 			mdrec->dfunc = &process_raid;
1377 			mdrec->un_self_id = uc.un_self_id;
1378 			mdrec->has_parent = MD_HAS_PARENT(
1379 			    uc.un_parent);
1380 			break;
1381 		case MDDB_F_TRANS_MASTER:
1382 			if (is_32bit_record) {
1383 				mt_unit32_od_t	*small_un;
1384 				mt_unit_t	*big_un;
1385 
1386 				small_un = (mt_unit32_od_t *)((uintptr_t)rbp +
1387 				    (sizeof (mddb_rb_t) - sizeof (int)));
1388 				newreqsize = sizeof (mt_unit_t);
1389 				big_un = (void *)Zalloc(newreqsize);
1390 				trans_master_convert((caddr_t)small_un,
1391 				    (caddr_t)big_un, SMALL_2_BIG);
1392 				mdrec->record = (void *)big_un;
1393 			} else {
1394 				mt_unit_t	*big_un;
1395 
1396 				big_un = (mt_unit_t *)((uintptr_t)rbp +
1397 				    (sizeof (mddb_rb_t) - sizeof (int)));
1398 				newreqsize = sizeof (mt_unit_t);
1399 				mdrec->record = (void *)Zalloc(newreqsize);
1400 				bcopy(big_un, mdrec->record, newreqsize);
1401 			}
1402 			uc = ((mt_unit_t *)mdrec->record)->c;
1403 			mdrec->dfunc = &process_trans;
1404 			mdrec->un_self_id = uc.un_self_id;
1405 			mdrec->has_parent = MD_HAS_PARENT(
1406 			    uc.un_parent);
1407 			break;
1408 		case MDDB_F_HOTSPARE:
1409 			if (is_32bit_record) {
1410 				hot_spare32_od_t	*small_un;
1411 				hot_spare_t		*big_un;
1412 
1413 				small_un = (hot_spare32_od_t *)((uintptr_t)rbp +
1414 				    (sizeof (mddb_rb_t) - sizeof (int)));
1415 				newreqsize = sizeof (hot_spare_t);
1416 				big_un = (void *)Zalloc(newreqsize);
1417 				hs_convert((caddr_t)small_un,
1418 				    (caddr_t)big_un, SMALL_2_BIG);
1419 				mdrec->record = (void *)big_un;
1420 			} else {
1421 				hot_spare_t		*big_un;
1422 
1423 				big_un = (hot_spare_t *)((uintptr_t)rbp +
1424 				    (sizeof (mddb_rb_t) - sizeof (int)));
1425 				newreqsize = sizeof (hot_spare_t);
1426 				mdrec->record = (void *)Zalloc(newreqsize);
1427 				bcopy(big_un, mdrec->record, newreqsize);
1428 			}
1429 			hs = (hot_spare_t *)mdrec->record;
1430 			mdrec->dfunc = &process_hotspare;
1431 			mdrec->un_self_id = NULL;
1432 			mdrec->hs_record_id = hs->hs_record_id;
1433 			mdrec->has_parent = 1;
1434 			break;
1435 		case MDDB_F_HOTSPARE_POOL:
1436 			/*
1437 			 * Ondisk and incore records are always same size.
1438 			 */
1439 			hsp = (hot_spare_pool_ond_t *)((uintptr_t)rbp +
1440 			    (sizeof (mddb_rb_t) - sizeof (int)));
1441 			newreqsize = sizeof (hot_spare_pool_ond_t) +
1442 			    (sizeof (mddb_recid_t) * hsp->hsp_nhotspares);
1443 			mdrec->record = (void *)Zalloc(newreqsize);
1444 			bcopy(hsp, mdrec->record, newreqsize);
1445 			hsp = (hot_spare_pool_ond_t *)mdrec->record;
1446 			mdrec->dfunc = &process_hotspare_pool;
1447 			/*
1448 			 * If the hsp has descriptive name we'll get
1449 			 * the un_self_id
1450 			 */
1451 			if (HSP_ID_IS_FN(hsp->hsp_self_id))
1452 				mdrec->un_self_id = hsp->hsp_self_id;
1453 			else
1454 				mdrec->un_self_id = NULL;
1455 			mdrec->has_parent = 0;
1456 			break;
1457 		/* All valid cases have been dealt with */
1458 		default:
1459 			(void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1460 			return (-1);
1461 	}
1462 
1463 	/*
1464 	 * If metadevice record has an entry in the NM namespace
1465 	 * then it is copied into the mdrec->n_name field.
1466 	 */
1467 	if (mdrec->un_self_id != NULL) {
1468 		for (nmname = &nm_record->r_name[0]; nmname->n_key != 0;
1469 		/*LINTED*/
1470 		    nmname = (struct nm_name *)((char *)nmname +
1471 		    NAMSIZ(nmname))) {
1472 			/*
1473 			 * Extract the metadevice/hsp name if it is
1474 			 * in the namespace.
1475 			 *
1476 			 * If it is a hot spare pool we will find our
1477 			 * match by comparing the NM record's n_key
1478 			 * with the extracted key from the hsp_self_id
1479 			 * Else, match the un_self_id for the record
1480 			 * to the n_minor name in the NM record.
1481 			 */
1482 			    if (mdrec->md_type == MDDB_F_HOTSPARE_POOL) {
1483 				if (nmname->n_key ==
1484 				    HSP_ID_TO_KEY(hsp->hsp_self_id)) {
1485 					mdrec->n_key = nmname->n_key;
1486 					uname = Strdup(nmname->n_name);
1487 					mdrec->n_name = uname;
1488 					break;
1489 				}
1490 			    } else {
1491 				if ((nmname->n_minor) == (uc.un_self_id)) {
1492 					(*mdrec).n_key = nmname->n_key;
1493 					uname = Strdup(nmname->n_name);
1494 					mdrec->n_name = uname;
1495 					break;
1496 				}
1497 			    }
1498 		}
1499 	}
1500 
1501 	/*
1502 	 * If the metadevice name is not in the namespace, then
1503 	 * then we will generate the name from the minor number
1504 	 * for the metadevice.  In the case of records for a hotspare
1505 	 * pool we use hsp_self_id, otherwise we use un_self_id.
1506 	 */
1507 	if (uname == NULL) {
1508 		if (mdrec->md_type == MDDB_F_HOTSPARE_POOL) {
1509 			uname = Malloc(MAXSIZEMDRECNAME);
1510 			(void) sprintf(uname, "hsp%03u",
1511 			    HSP_ID(hsp->hsp_self_id));
1512 			mdrec->n_name = uname;
1513 		} else if (mdrec->md_type != MDDB_F_HOTSPARE) {
1514 			/*
1515 			 * Generate the metadevice name for all other records
1516 			 * (except for hotspares, because hotspares can only
1517 			 * be physical devices.)
1518 			 */
1519 			uname = Malloc(MAXSIZEMDRECNAME);
1520 			(void) sprintf(uname, "d%lu",
1521 			    MD_MIN2UNIT(mdrec->un_self_id));
1522 			mdrec->n_name = uname;
1523 		}
1524 	}
1525 
1526 	return (0);
1527 }
1528 
1529 
1530 /*
1531  * read_mdrecord()
1532  *
1533  * Reads the mddb_rb32_od_t or mddb_rb_t and the associated metadevice record
1534  * from the disk.  Runs magic, checksum, and revision checks on the record
1535  * block.
1536  *
1537  * Returns:
1538  *	< 0 for failure
1539  *	  0 for success
1540  *
1541  */
1542 static int
read_mdrecord(md_im_rec_t ** mdimpp,mddb_mb_t * mbp,mddb_rb_t * nm,mddb_de_t * dep,char * diskname,int fd,md_timeval32_t * lastaccess,md_error_t * ep)1543 read_mdrecord(
1544 	md_im_rec_t	**mdimpp,
1545 	mddb_mb_t	*mbp,
1546 	mddb_rb_t	*nm,
1547 	mddb_de_t	*dep,
1548 	char		*diskname,
1549 	int 		fd,
1550 	md_timeval32_t	*lastaccess,
1551 	md_error_t 	*ep
1552 )
1553 {
1554 	int		cnt, rval = 0;
1555 	daddr_t		pblk;
1556 	md_im_rec_t	*tmp_mdrec;
1557 	void 		*rbp = NULL;
1558 	char 		*rbp_tmp = NULL;
1559 	mddb_rb32_t	*rbp_32;
1560 	mddb_rb_t	*rbp_64;
1561 	crc_skip_t	*skip = NULL;
1562 	int		is_32bit_record;
1563 
1564 	tmp_mdrec = Zalloc(sizeof (md_im_rec_t));
1565 	rbp = (void *)Zalloc(dbtob(dep->de_blkcount));
1566 	rbp_tmp = (char *)rbp;
1567 
1568 	/* Read in the appropriate record and return configurations */
1569 	for (cnt = 0; cnt < dep->de_blkcount; cnt++) {
1570 		if ((pblk = getphysblk(dep->de_blks[cnt], mbp)) < 0) {
1571 			rval = mdmddberror(ep, MDE_DB_BLKRANGE,
1572 			    NODEV32, MD_LOCAL_SET,
1573 			    dep->de_blks[cnt], diskname);
1574 			return (rval);
1575 		}
1576 
1577 		if (lseek(fd, (off_t)dbtob(pblk), SEEK_SET) < 0) {
1578 			rval = mdsyserror(ep, errno, diskname);
1579 			return (rval);
1580 		}
1581 
1582 		if (read(fd, rbp_tmp, DEV_BSIZE) != DEV_BSIZE) {
1583 			rval = mdsyserror(ep, errno, diskname);
1584 			return (rval);
1585 		}
1586 
1587 		rbp_tmp += DEV_BSIZE;
1588 	}
1589 	tmp_mdrec->md_type = dep->de_flags;
1590 
1591 	/*
1592 	 * The only place to discover whether or not the record is a
1593 	 * 32bit or 64bit record is from the record's rb_revision field.
1594 	 * The mddb_rb_t and mddb_rb32_t structures are identical for the
1595 	 * following fields:
1596 	 *	rb_magic, rb_revision, rb_checksum, and rb_checksum_fiddle.
1597 	 * So we can assume that the record is a 32bit structure when we
1598 	 * check the record's magic number and revision and when we calculate
1599 	 * the records checksum.
1600 	 */
1601 	rbp_32 = (mddb_rb32_t *)rbp;
1602 
1603 	/*
1604 	 * Checking the magic number for the record block.
1605 	 */
1606 	if (rbp_32->rb_magic != MDDB_MAGIC_RB) {
1607 		rval = -1;
1608 		(void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1609 		goto out;
1610 	}
1611 
1612 	/*
1613 	 * Checking the revision for the record block. Must match either
1614 	 * revision for the current 64bit or 32bit record block.  Also,
1615 	 * setting the flag for whether or not it is a 32bit record.
1616 	 */
1617 	is_32bit_record = 0;
1618 	switch (rbp_32->rb_revision) {
1619 	case MDDB_REV_RB:
1620 	case MDDB_REV_RBFN:
1621 		is_32bit_record = 1;
1622 		break;
1623 	case MDDB_REV_RB64:
1624 	case MDDB_REV_RB64FN:
1625 		break;
1626 	default:
1627 		rval = -1;
1628 		(void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1629 		goto out;
1630 	}
1631 
1632 	/*
1633 	 * Calculating the checksum for this record block. Need
1634 	 * to skip the rb's checksum fiddle.
1635 	 */
1636 	skip = (crc_skip_t *)Malloc(sizeof (crc_skip_t));
1637 	skip->skip_next = NULL;
1638 	skip->skip_offset = offsetof(mddb_rb_t, rb_checksum_fiddle);
1639 	skip->skip_size = 3 * sizeof (uint_t);
1640 	if (crcchk(rbp_32, &rbp_32->rb_checksum, dep->de_recsize, skip)) {
1641 		rval = -1;
1642 		(void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1643 		goto out;
1644 	}
1645 
1646 	/* mddb_rb_t and mddb_rb32_t differ before the rb_timestamp field */
1647 	if (!is_32bit_record) {
1648 		if ((*lastaccess).tv_sec < rbp_32->rb_timestamp.tv_sec) {
1649 		    *lastaccess = rbp_32->rb_timestamp;
1650 		} else if ((*lastaccess).tv_sec ==
1651 		    rbp_32->rb_timestamp.tv_sec) {
1652 			if ((*lastaccess).tv_usec <
1653 			    rbp_32->rb_timestamp.tv_usec)
1654 				*lastaccess = rbp_32->rb_timestamp;
1655 		}
1656 	} else {
1657 		rbp_64 = (mddb_rb_t *)rbp;
1658 		if ((*lastaccess).tv_sec < rbp_64->rb_timestamp.tv_sec) {
1659 		    *lastaccess = rbp_64->rb_timestamp;
1660 		} else if ((*lastaccess).tv_sec ==
1661 		    rbp_64->rb_timestamp.tv_sec) {
1662 			if ((*lastaccess).tv_usec <
1663 			    rbp_64->rb_timestamp.tv_usec)
1664 				*lastaccess = rbp_64->rb_timestamp;
1665 		}
1666 	}
1667 
1668 	/* Populates the fields in md_im_rec_t *tmp_mdrec. */
1669 	rval = extract_mduser_data(nm, tmp_mdrec, rbp, is_32bit_record, ep);
1670 	if (rval < 0)
1671 		goto out;
1672 
1673 	/* Adding record to the head of the list of all metadevices. */
1674 	tmp_mdrec->prev = NULL;
1675 	if (*mdimpp == NULL) {
1676 		tmp_mdrec->next = NULL;
1677 		*mdimpp = tmp_mdrec;
1678 	} else {
1679 		(*mdimpp)->prev = tmp_mdrec;
1680 		tmp_mdrec->next = *mdimpp;
1681 		*mdimpp = tmp_mdrec;
1682 	}
1683 
1684 out:
1685 	/* Free the skip list */
1686 	while (skip) {
1687 		crc_skip_t	*skip_rm = skip;
1688 
1689 		skip = skip->skip_next;
1690 		Free(skip_rm);
1691 	}
1692 
1693 	if (rbp)
1694 		Free(rbp);
1695 
1696 	return (rval);
1697 }
1698 
1699 
1700 /*
1701  * read_all_mdrecords()
1702  *
1703  * Reads the directory block and directory entries.
1704  * Runs magic, checksum, and revision checks on the directory block.
1705  *
1706  * Returns:
1707  *	< 0 for failure
1708  *	  0 for success
1709  */
1710 static int
read_all_mdrecords(md_im_rec_t ** mdimpp,mddb_mb_t * mbp,mddb_lb_t * lbp,mddb_rb_t * nm,mdname_t * rsp,int fd,md_timeval32_t * lastaccess,md_error_t * ep)1711 read_all_mdrecords(
1712 	md_im_rec_t	**mdimpp,
1713 	mddb_mb_t	*mbp,
1714 	mddb_lb_t	*lbp,
1715 	mddb_rb_t	*nm,
1716 	mdname_t	*rsp,
1717 	int 		fd,
1718 	md_timeval32_t	*lastaccess,
1719 	md_error_t 	*ep
1720 )
1721 {
1722 	int		dbblk, rval = 0;
1723 	char		db[DEV_BSIZE];
1724 	mddb_de_t	*dep;
1725 	int		desize;
1726 	/*LINTED*/
1727 	mddb_db_t	*dbp = (mddb_db_t *)&db;
1728 
1729 	/* Read in all directory blocks */
1730 	for (dbblk = lbp->lb_dbfirstblk;
1731 	    dbblk != 0;
1732 	    dbblk = dbp->db_nextblk) {
1733 
1734 		if ((rval = read_database_block(ep, fd, mbp, dbblk,
1735 		    dbp, sizeof (db))) <= 0)
1736 			goto out;
1737 
1738 		/*
1739 		 * Set ep with error code for MDE_DB_NODB.  This is the
1740 		 * error code used in the kernel when there is a problem
1741 		 * with reading records in.  Checks the magic number, the
1742 		 * revision, and the checksum for each directory block.
1743 		 */
1744 		if (dbp->db_magic != MDDB_MAGIC_DB) {
1745 			rval = -1;
1746 			(void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1747 			goto out;
1748 		}
1749 
1750 		if (revchk(MDDB_REV_DB, dbp->db_revision)) {
1751 			rval = -1;
1752 			(void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1753 			goto out;
1754 		}
1755 
1756 		if (crcchk(dbp, &dbp->db_checksum, MDDB_BSIZE, NULL)) {
1757 			rval = -1;
1758 			(void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1759 			goto out;
1760 		}
1761 
1762 		/*
1763 		 * If db timestamp is more recent than the previously recorded
1764 		 * last modified timestamp, then update last modified.
1765 		 */
1766 		if ((*lastaccess).tv_sec < dbp->db_timestamp.tv_sec) {
1767 			*lastaccess = dbp->db_timestamp;
1768 		} else if ((*lastaccess).tv_sec == dbp->db_timestamp.tv_sec) {
1769 			if ((*lastaccess).tv_usec < dbp->db_timestamp.tv_usec)
1770 				*lastaccess = dbp->db_timestamp;
1771 		}
1772 
1773 		/* Creates dep list of all directory entries in the db */
1774 		if (dbp->db_firstentry != NULL) {
1775 			/* LINTED */
1776 			dep = (mddb_de_t *)((caddr_t)(&dbp->db_firstentry)
1777 			    + sizeof (dbp->db_firstentry));
1778 			dbp->db_firstentry = dep;
1779 			while (dep && dep->de_next) {
1780 				desize = sizeof (*dep) -
1781 				    sizeof (dep->de_blks) +
1782 				    sizeof (daddr_t) * dep->de_blkcount;
1783 				/* LINTED */
1784 				dep->de_next = (mddb_de_t *)
1785 				    ((caddr_t)dep + desize);
1786 				dep = dep->de_next;
1787 			}
1788 		}
1789 
1790 		/*
1791 		 * Process all directory entries in the directory block.
1792 		 * For each directory entry, read_mdrec is called to read
1793 		 * in the record data.
1794 		 */
1795 		for (dep = dbp->db_firstentry; dep != NULL;
1796 		    dep = dep->de_next) {
1797 
1798 			/*
1799 			 * de_flags is set to the type of metadevice.
1800 			 * If directory entry does not correspond to a
1801 			 * specific metadevice then it is set to zero.
1802 			 * All namespace records(NM, SHR_NM, DID_SHR_NM) have a
1803 			 * value of zero in their de_flags field.
1804 			 */
1805 			if ((dep->de_flags != 0)&&
1806 			    (dep->de_flags != MDDB_F_OPT) &&
1807 			    (dep->de_flags != MDDB_F_TRANS_LOG) &&
1808 			    (dep->de_flags != MDDB_F_CHANGELOG)) {
1809 				rval = read_mdrecord(mdimpp, mbp, nm, dep,
1810 				    rsp->cname, fd, lastaccess, ep);
1811 				if (rval < 0)
1812 					goto out;
1813 			}
1814 		}
1815 	}
1816 
1817 out:
1818 	return (rval);
1819 }
1820 
1821 
1822 /*
1823  * report_metastat_info()
1824  *
1825  * Generates the metastat -c output.  Also, updates the global variable
1826  * for a last accessed timestamp.
1827  *
1828  * Returns:
1829  *	< 0 for failure
1830  *	  0 for success
1831  *
1832  */
1833 int
report_metastat_info(mddb_mb_t * mb,mddb_lb_t * lbp,mddb_rb_t * nm,pnm_rec_t ** pnm,mdname_t * rsp,int fd,md_timeval32_t * lastaccess,md_error_t * ep)1834 report_metastat_info(
1835 	mddb_mb_t		*mb,
1836 	mddb_lb_t		*lbp,
1837 	mddb_rb_t		*nm,
1838 	pnm_rec_t		**pnm,
1839 	mdname_t		*rsp,
1840 	int			fd,
1841 	md_timeval32_t		*lastaccess,
1842 	md_error_t		*ep
1843 )
1844 {
1845 	int rval = 0;
1846 	/* list of all metadevices in diskset */
1847 	md_im_rec_t	*mdimp = NULL;
1848 	md_im_rec_t	*tmp_mdrec, *rm_mdrec;
1849 
1850 	/* Read in metadevice records and add entries to mdimp list. */
1851 	rval = read_all_mdrecords(&mdimp, mb, lbp, nm, rsp, fd, lastaccess,
1852 	    ep);
1853 	if (rval < 0)
1854 		goto out;
1855 
1856 	/* Adding a fake record to the head of the list of all metadevices. */
1857 	if (mdimp != NULL) {
1858 		tmp_mdrec = Zalloc(sizeof (md_im_rec_t));
1859 		tmp_mdrec->prev = NULL;
1860 		mdimp->prev = tmp_mdrec;
1861 		tmp_mdrec->next = mdimp;
1862 		mdimp = tmp_mdrec;
1863 	}
1864 
1865 	/* Calling functions to process all metadevices on mdimp list */
1866 	process_toplevel_devices(&mdimp, *pnm, MDDB_F_SOFTPART);
1867 	process_toplevel_devices(&mdimp, *pnm, MDDB_F_TRANS_MASTER);
1868 	process_toplevel_devices(&mdimp, *pnm, MDDB_F_MIRROR);
1869 	process_toplevel_devices(&mdimp, *pnm, MDDB_F_RAID);
1870 	process_toplevel_devices(&mdimp, *pnm, MDDB_F_STRIPE);
1871 	process_toplevel_devices(&mdimp, *pnm, MDDB_F_HOTSPARE_POOL);
1872 	(void) printf("\n");
1873 
1874 out:
1875 	/*
1876 	 * If mdreclist is not null, then this will walk through all
1877 	 * elements and free them.
1878 	 */
1879 	tmp_mdrec = mdimp;
1880 	while (tmp_mdrec != NULL) {
1881 		rm_mdrec = tmp_mdrec;
1882 		tmp_mdrec = tmp_mdrec->next;
1883 		if (rm_mdrec->record != NULL)
1884 			Free(rm_mdrec->record);
1885 		if (rm_mdrec->n_name != NULL)
1886 			Free(rm_mdrec->n_name);
1887 		Free(rm_mdrec);
1888 	}
1889 
1890 	free_pnm_rec_list(pnm);
1891 	return (rval);
1892 }
1893