xref: /titanic_41/usr/src/lib/lvm/libmeta/common/meta_trans.c (revision af3025fd013117424096c94bd9e116ecc538cc6b)
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 /*
29  * Just in case we're not in a build environment, make sure that
30  * TEXT_DOMAIN gets set to something.
31  */
32 #if !defined(TEXT_DOMAIN)
33 #define	TEXT_DOMAIN "SYS_TEST"
34 #endif
35 
36 /*
37  * trans operations
38  */
39 
40 #include <meta.h>
41 #include <meta_basic.h>
42 #include <sys/lvm/md_trans.h>
43 #include <sys/wait.h>
44 #include <sys/mnttab.h>
45 #include <stddef.h>
46 
47 extern char *getfullblkname();
48 
49 /*
50  * replace trans
51  */
52 
53 int
54 meta_trans_replace(mdsetname_t *sp, mdname_t *transnp, mdname_t *oldnp,
55     mdname_t *newnp, mdcmdopts_t options, md_error_t *ep)
56 {
57 	replace_params_t	params;
58 	md_dev64_t		old_dev,
59 				new_dev;
60 	daddr_t			new_start_blk,
61 				new_end_blk;
62 
63 	/* should have same set */
64 	assert(sp != NULL);
65 	assert(sp->setno == MD_MIN2SET(meta_getminor(transnp->dev)));
66 
67 	new_dev = newnp->dev;
68 	new_start_blk = newnp->start_blk;
69 	new_end_blk = newnp->end_blk;
70 
71 	meta_invalidate_name(transnp);
72 	/* the old device binding is now established */
73 	if ((old_dev = oldnp->dev) == NODEV64)
74 		return (mdsyserror(ep, ENODEV, oldnp->cname));
75 
76 	if (((strcmp(oldnp->rname, newnp->rname) == 0) &&
77 	    (old_dev != new_dev))) {
78 		newnp->dev = new_dev;
79 		newnp->start_blk = new_start_blk;
80 		newnp->end_blk = new_end_blk;
81 	}
82 
83 	if (add_key_name(sp, newnp, NULL, ep) != 0)
84 		return (-1);
85 
86 	(void) memset(&params, 0, sizeof (params));
87 	params.mnum = meta_getminor(transnp->dev);
88 	MD_SETDRIVERNAME(&params, MD_TRANS, sp->setno);
89 
90 	params.cmd = REPLACE_COMP;
91 	params.old_dev = old_dev;
92 	params.new_dev = new_dev;
93 	params.new_key = newnp->key;
94 	if (metaioctl(MD_IOCREPLACE, &params, &params.mde, NULL) != 0) {
95 		(void) del_key_name(sp, newnp, ep);
96 		return (mdstealerror(ep, &params.mde));
97 	}
98 	meta_invalidate_name(oldnp);
99 	meta_invalidate_name(newnp);
100 	meta_invalidate_name(transnp);
101 
102 	if (options & MDCMD_PRINT) {
103 		(void) printf(dgettext(TEXT_DOMAIN,
104 		    "%s: device %s is replaced with %s\n"),
105 		    transnp->cname, oldnp->cname, newnp->cname);
106 	}
107 	return (0);
108 }
109 
110 
111 
112 /*
113  * FUNCTION:	meta_get_trans_names()
114  * INPUT:	sp	- the set name to get trans from
115  *		options	- options from the command line
116  * OUTPUT:	nlpp	- list of all trans names
117  *		ep	- return error pointer
118  * RETURNS:	int	- -1 if error, 0 success
119  * PURPOSE:	returns a list of all trans in the metadb
120  *		for all devices in the specified set
121  */
122 int
123 meta_get_trans_names(
124 	mdsetname_t	*sp,
125 	mdnamelist_t	**nlpp,
126 	int		options,
127 	md_error_t	*ep
128 )
129 {
130 	return (meta_get_names(MD_TRANS, sp, nlpp, options, ep));
131 }
132 
133 /*
134  * free trans unit
135  */
136 void
137 meta_free_trans(
138 	md_trans_t	*transp
139 )
140 {
141 	Free(transp);
142 }
143 
144 /*
145  * get trans (common)
146  */
147 md_trans_t *
148 meta_get_trans_common(
149 	mdsetname_t	*sp,
150 	mdname_t	*transnp,
151 	int		fast,
152 	md_error_t	*ep
153 )
154 {
155 	mddrivename_t	*dnp = transnp->drivenamep;
156 	char		*miscname;
157 	mt_unit_t	*mt;
158 	md_trans_t	*transp;
159 	int		gotlog;
160 
161 	/* must have set */
162 	assert(sp != NULL);
163 	assert(sp->setno == MD_MIN2SET(meta_getminor(transnp->dev)));
164 
165 	/* short circuit */
166 	if (dnp->unitp != NULL) {
167 		assert(dnp->unitp->type == MD_METATRANS);
168 		return ((md_trans_t *)dnp->unitp);
169 	}
170 
171 	/* get miscname and unit */
172 	if ((miscname = metagetmiscname(transnp, ep)) == NULL)
173 		return (NULL);
174 	if (strcmp(miscname, MD_TRANS) != 0) {
175 		(void) mdmderror(ep, MDE_NOT_MT,
176 		    meta_getminor(transnp->dev), transnp->cname);
177 		return (NULL);
178 	}
179 	if ((mt = (mt_unit_t *)meta_get_mdunit(sp, transnp, ep)) == NULL)
180 		return (NULL);
181 	assert(mt->c.un_type == MD_METATRANS);
182 
183 	/* allocate trans */
184 	transp = Zalloc(sizeof (*transp));
185 
186 	/* get common info */
187 	transp->common.namep = transnp;
188 	transp->common.type = mt->c.un_type;
189 	transp->common.state = mt->c.un_status;
190 	transp->common.capabilities = mt->c.un_capabilities;
191 	transp->common.parent = mt->c.un_parent;
192 	transp->common.size = mt->c.un_total_blocks;
193 	transp->common.user_flags = mt->c.un_user_flags;
194 	transp->common.revision = mt->c.un_revision;
195 
196 	/* get master */
197 	transp->masternamep = metakeyname(&sp, mt->un_m_key, fast, ep);
198 	if (transp->masternamep == NULL)
199 		goto out;
200 
201 	/* get log */
202 	gotlog = ((mt->un_flags & TRANS_DETACHED) == 0);
203 	if (gotlog) {
204 		daddr_t	sblk;
205 
206 		transp->lognamep = metakeyname(&sp, mt->un_l_key, fast, ep);
207 		if (transp->lognamep == NULL)
208 			goto out;
209 
210 		/* calculate the kernels start block */
211 		sblk = mt->un_l_pwsblk + mt->un_l_maxtransfer;
212 
213 		if (getenv("META_DEBUG_START_BLK") != NULL) {
214 			if (metagetstart(sp, transp->lognamep, ep) ==
215 			    MD_DISKADDR_ERROR)
216 				mdclrerror(ep);
217 
218 			if (transp->lognamep->start_blk > sblk)
219 				md_eprintf(dgettext(TEXT_DOMAIN,
220 				    "%s: suspected bad start block [trans]\n"),
221 				    transp->lognamep->cname);
222 		}
223 
224 		/* override any start_blk */
225 		transp->lognamep->start_blk = sblk;
226 	}
227 
228 	/* get flags, etc. */
229 	transp->flags = mt->un_flags;
230 	transp->timestamp = mt->un_timestamp;
231 	transp->log_error = mt->un_l_error;
232 	transp->log_timestamp = mt->un_l_timestamp;
233 	transp->log_size = mt->un_l_nblks;
234 	transp->debug = mt->un_debug;
235 
236 	/* cleanup, return success */
237 	Free(mt);
238 	dnp->unitp = (md_common_t *)transp;
239 	return (transp);
240 
241 	/* cleanup, return error */
242 out:
243 	Free(mt);
244 	meta_free_trans(transp);
245 	return (NULL);
246 }
247 
248 /*
249  * get trans
250  */
251 md_trans_t *
252 meta_get_trans(
253 	mdsetname_t	*sp,
254 	mdname_t	*transnp,
255 	md_error_t	*ep
256 )
257 {
258 	return (meta_get_trans_common(sp, transnp, 0, ep));
259 }
260 
261 /*
262  * check trans for dev
263  */
264 static int
265 in_trans(
266 	mdsetname_t	*sp,
267 	mdname_t	*transnp,
268 	mdname_t	*np,
269 	mdchkopts_t	options,
270 	diskaddr_t	slblk,
271 	diskaddr_t	nblks,
272 	md_error_t	*ep
273 )
274 {
275 	md_trans_t	*transp;
276 	mdname_t	*masternp;
277 	mdname_t	*lognp;
278 
279 	/* should be in the same set */
280 	assert(sp != NULL);
281 	assert(sp->setno == MD_MIN2SET(meta_getminor(transnp->dev)));
282 
283 	/* get unit */
284 	if ((transp = meta_get_trans(sp, transnp, ep)) == NULL)
285 		return (-1);
286 
287 	/* check master */
288 	masternp = transp->masternamep;
289 	if ((! metaismeta(masternp)) &&
290 	    (meta_check_overlap(transnp->cname, np, slblk, nblks,
291 	    masternp, 0, -1, ep) != 0)) {
292 		return (-1);
293 	}
294 
295 	/* check log */
296 	if (((lognp = transp->lognamep) != NULL) &&
297 	    (! (options & MDCHK_ALLOW_LOG)) &&
298 	    (! metaismeta(lognp))) {
299 		daddr_t		log_start;
300 		int		err;
301 
302 		/* check same drive since metagetstart() can fail */
303 		if ((err = meta_check_samedrive(np, lognp, ep)) < 0)
304 			return (-1);
305 
306 		/* check overlap */
307 		if (err != 0) {
308 			if ((log_start = metagetstart(sp, lognp, ep)) ==
309 			    MD_DISKADDR_ERROR)
310 				return (-1);
311 			if (meta_check_overlap(transnp->cname, np, slblk,
312 			    nblks, lognp, log_start, -1, ep) != 0) {
313 				return (-1);
314 			}
315 		}
316 	}
317 
318 	/* return success */
319 	return (0);
320 }
321 
322 /*
323  * check to see if we're in a trans
324  */
325 int
326 meta_check_intrans(
327 	mdsetname_t	*sp,
328 	mdname_t	*np,
329 	mdchkopts_t	options,
330 	diskaddr_t	slblk,
331 	diskaddr_t	nblks,
332 	md_error_t	*ep
333 )
334 {
335 	mdnamelist_t	*transnlp = NULL;
336 	mdnamelist_t	*p;
337 	int		rval = 0;
338 
339 	/* should have a set */
340 	assert(sp != NULL);
341 
342 	/* for each trans */
343 	if (meta_get_trans_names(sp, &transnlp, 0, ep) < 0)
344 		return (-1);
345 	for (p = transnlp; (p != NULL); p = p->next) {
346 		mdname_t	*transnp = p->namep;
347 
348 		/* check trans */
349 		if (in_trans(sp, transnp, np, options, slblk, nblks, ep) != 0) {
350 			rval = -1;
351 			break;
352 		}
353 	}
354 
355 	/* cleanup, return success */
356 	metafreenamelist(transnlp);
357 	return (rval);
358 }
359 
360 /*
361  * check master
362  */
363 int
364 meta_check_master(
365 	mdsetname_t	*sp,
366 	mdname_t	*np,
367 	int		force,
368 	md_error_t	*ep
369 )
370 {
371 	mdchkopts_t	options = 0;
372 	md_common_t	*mdp;
373 
374 	/* make sure we have a disk */
375 	if (metachkdisk(np, ep) != 0)
376 		return (-1);
377 
378 	/* check to ensure that it is not already in use */
379 	if ((!force) && meta_check_inuse(sp, np, MDCHK_INUSE, ep) != 0) {
380 		return (-1);
381 	}
382 
383 	/* make sure it is in the set */
384 	if (meta_check_inset(sp, np, ep) != 0)
385 		return (-1);
386 
387 	/* make sure its not in a metadevice */
388 	if (! metaismeta(np)) {		/* Non-metadevices */
389 		if (meta_check_inmeta(sp, np, options, 0, -1, ep) != 0)
390 			return (-1);
391 	} else {			/* Metadevices only! */
392 		if ((mdp = meta_get_unit(sp, np, ep)) == NULL)
393 			return (-1);
394 
395 		/*
396 		 * Since soft partitions may appear at the top or bottom
397 		 * of the metadevice stack, we check them separately.
398 		 * A trans may be built on top of a soft partition if
399 		 * the soft partition has no parent (can't rely on the
400 		 * MD_CAN_PARENT flag in this case since a soft partition
401 		 * built on a metadevice clears this flag to prevent nested
402 		 * configurations).
403 		 */
404 		if ((meta_sp_issp(sp, np, ep) == 0) &&
405 		    (mdp->parent == MD_NO_PARENT))
406 			return (0);
407 
408 		if ((! (mdp->capabilities & MD_CAN_PARENT)) ||
409 		    (mdp->parent != MD_NO_PARENT)) {
410 			return (mdmderror(ep, MDE_INVAL_UNIT,
411 			    meta_getminor(np->dev), np->cname));
412 		}
413 	}
414 
415 	/* return success */
416 	return (0);
417 }
418 
419 /*
420  * check log
421  */
422 int
423 meta_check_log(
424 	mdsetname_t	*sp,
425 	mdname_t	*np,
426 	md_error_t	*ep
427 )
428 {
429 	mdchkopts_t	options = (MDCHK_ALLOW_MDDB | MDCHK_ALLOW_LOG);
430 	md_common_t	*mdp;
431 
432 	/* make sure we have a disk */
433 	if (metachkdisk(np, ep) != 0)
434 		return (-1);
435 
436 	/* check to ensure that it is not already in use */
437 	if (meta_check_inuse(sp, np, MDCHK_INUSE, ep) != 0) {
438 		return (-1);
439 	}
440 
441 	/* make sure it is in the set */
442 	if (meta_check_inset(sp, np, ep) != 0)
443 		return (-1);
444 
445 	/* make sure its not in a metadevice */
446 	if (! metaismeta(np)) {		/* Non-metadevices */
447 		if (meta_check_inmeta(sp, np, options, 0, -1, ep) != 0)
448 			return (-1);
449 	} else {			/* Metadevices only! */
450 		if ((mdp = meta_get_unit(sp, np, ep)) == NULL)
451 			return (-1);
452 
453 		/*
454 		 * Since soft partitions may appear at the top or bottom
455 		 * of the metadevice stack, we check them separately.
456 		 * A trans may be built on top of a soft partition if
457 		 * the soft partition has no parent (can't rely on the
458 		 * MD_CAN_PARENT flag in this case since a soft partition
459 		 * built on a metadevice clears this flag to prevent nested
460 		 * configurations).
461 		 *
462 		 */
463 		if ((meta_sp_issp(sp, np, ep) == 0) &&
464 		    (mdp->parent == MD_NO_PARENT))
465 			return (0);
466 
467 		if ((! (mdp->capabilities & MD_CAN_PARENT)) ||
468 		    ((mdp->parent != MD_NO_PARENT) &&
469 		    (mdp->parent != MD_MULTI_PARENT))) {
470 			return (mdmderror(ep, MDE_INVAL_UNIT,
471 			    meta_getminor(np->dev), np->cname));
472 		}
473 	}
474 
475 	/* return success */
476 	return (0);
477 }
478 
479 /*
480  * print trans
481  */
482 static int
483 trans_print(
484 	md_trans_t	*transp,
485 	char		*fname,
486 	FILE		*fp,
487 	md_error_t	*ep
488 )
489 {
490 	int		rval = -1;
491 
492 	/* print name and -t */
493 	if (fprintf(fp, "%s -t", transp->common.namep->cname) == EOF)
494 		goto out;
495 
496 	/* print master */
497 	/*
498 	 * If the path is our standard /dev/rdsk or /dev/md/rdsk
499 	 * then just print out the cxtxdxsx or the dx, metainit
500 	 * will assume the default, otherwise we need the full
501 	 * pathname to make sure this works as we intend.
502 	 */
503 	if ((strstr(transp->masternamep->rname, "/dev/rdsk") == NULL) &&
504 	    (strstr(transp->masternamep->rname, "/dev/md/rdsk") == NULL) &&
505 	    (strstr(transp->masternamep->rname, "/dev/td/") == NULL)) {
506 		/* not standard path, print full pathname */
507 		if (fprintf(fp, " %s", transp->masternamep->rname) == EOF)
508 			goto out;
509 	} else {
510 		/* standard path, print ctds or d number */
511 		if (fprintf(fp, " %s", transp->masternamep->cname) == EOF)
512 			goto out;
513 	}
514 
515 
516 	/* print log */
517 	if (transp->lognamep != NULL) {
518 		/*
519 		 * If the path is our standard /dev/rdsk or /dev/md/rdsk
520 		 * then just print out the cxtxdxsx or the dx, metainit
521 		 * will assume the default, otherwise we need the full
522 		 * pathname to make sure this works as we intend.
523 		 */
524 		if ((strstr(transp->lognamep->rname, "/dev/rdsk") == NULL) &&
525 		    (strstr(transp->lognamep->rname, "/dev/md/rdsk") == NULL) &&
526 		    (strstr(transp->lognamep->rname, "/dev/td/") == NULL)) {
527 			/* not standard path, print full pathname */
528 			if (fprintf(fp, " %s", transp->lognamep->rname) == EOF)
529 				goto out;
530 		} else {
531 			/* standard path */
532 			if (fprintf(fp, " %s", transp->lognamep->cname) == EOF)
533 				goto out;
534 		}
535 	}
536 
537 	/* print terminating newline */
538 	if (fprintf(fp, "\n") == EOF)
539 		goto out;
540 
541 	/* success */
542 	rval = 0;
543 
544 	/* cleanup, return error */
545 out:
546 	if (rval != 0)
547 		(void) mdsyserror(ep, errno, fname);
548 	return (rval);
549 }
550 
551 /*
552  * convert flags to repair action
553  */
554 
555 char *
556 mt_flags_to_action(
557 	md_trans_t *transp
558 )
559 {
560 	int	 len;
561 	char	*actionp	= NULL;
562 	int	 err		= -1;
563 
564 	if (!transp) {
565 		goto out;
566 	}
567 
568 	/*
569 	 * if in any of these states, the log_error word is not (yet) meaningful
570 	 */
571 	if (transp->flags & (TRANS_DETACHED|TRANS_DETACHING|TRANS_ATTACHING)) {
572 		goto out;
573 	}
574 
575 	if (transp->log_error & LDL_ANYERROR) {
576 		char *fix_msg = dgettext(TEXT_DOMAIN,
577 		    "    To Fix: Please refer to the log device's status.\n");
578 
579 		if ((len = strlen(fix_msg)) <= 0) {
580 			goto out;
581 		}
582 		if (!(actionp = Zalloc(len+1))) {
583 			goto out;
584 		}
585 		if (strncpy(actionp, fix_msg, len + 1) != actionp) {
586 			goto out;
587 		}
588 	}
589 	err = 0;
590 out:
591 	if (err != 0) {
592 		if (actionp) {
593 			Free(actionp);
594 			actionp = NULL;
595 		}
596 	}
597 	return (actionp);
598 }
599 
600 /*
601  * convert log state to repair action
602  */
603 char *
604 mt_l_error_to_action(
605 	mdsetname_t	*sp,
606 	mdnamelist_t	*transnlp,
607 	mdname_t	*lognamep,
608 	md_error_t	*ep
609 )
610 {
611 	char		 umnt_msg[1024];
612 	char		 fsck_msg[1024];
613 	char		 mnt_msg[1024];
614 	mdnamelist_t	*p;
615 	md_trans_t	*tp;
616 	int		 rc;
617 	int		 len		= 0;
618 	char		*rmsg		= NULL;
619 	char		*mp		= NULL;
620 	bool_t		 is_mounted	= FALSE;
621 	bool_t		 any_in_error	= FALSE;
622 	int		 only_fsck	= TRUE;
623 
624 	(void) memset(umnt_msg, 0, sizeof (umnt_msg));
625 	(void) memset(fsck_msg, 0, sizeof (fsck_msg));
626 	(void) memset(mnt_msg, 0, sizeof (mnt_msg));
627 
628 	/*
629 	 * If a the trans devices listed in transnlp contain
630 	 * devices which are in error and are sub-mount points
631 	 * of each other, than it would need to be reverse sorted.
632 	 * When this actually occurs, and customers find the usage
633 	 * message insufficiently clear, then we should take the
634 	 * hit to sort it.
635 	 */
636 
637 	/*
638 	 * this preliminary loop is necessary to keep the
639 	 * fsck message greppable, if possible
640 	 */
641 	for (p = transnlp; ((p != NULL) && (only_fsck == TRUE)); p = p->next) {
642 
643 		if ((tp = meta_get_trans(sp, p->namep, ep)) == NULL) {
644 			goto out;
645 		}
646 
647 		if (!(tp->log_error & LDL_ANYERROR)) {
648 			continue;
649 		}
650 
651 		if ((tp->lognamep == NULL) ||
652 		    (strcmp(lognamep->bname, tp->lognamep->bname) != 0)) {
653 			continue;
654 		}
655 
656 		mdclrerror(ep);
657 		is_mounted = (meta_check_inuse(sp,
658 		    p->namep, MDCHK_MOUNTED, ep) != 0);
659 
660 		if (!mdisok(ep) && mdiserror(ep, MDE_IS_MOUNTED)) {
661 			goto out;
662 		}
663 
664 		mdclrerror(ep);
665 		mp = meta_get_mountp(sp, p->namep, ep);
666 
667 		if (!mdisok(ep)) {
668 			goto out;
669 		}
670 
671 		if (is_mounted) {
672 			if (!mp) {
673 				goto out;
674 			}
675 			only_fsck = FALSE;
676 
677 			/*
678 			 * not greppable; there must be multiple commands, so
679 			 * add preliminary newline so the formatting is uniform
680 			 */
681 			if (sprintf(umnt_msg, "\n") == EOF) {
682 				goto out;
683 			}
684 
685 		}
686 
687 		if (mp) {
688 			Free(mp);
689 			mp = NULL;
690 		}
691 	}
692 
693 	/*
694 	 * although the log may either be in error or hard-error
695 	 * states, the action is the same; unmount, fsck and remount
696 	 * all fs associated with this log
697 	 */
698 	for (p = transnlp; (p != NULL); p = p->next) {
699 
700 		if ((tp = meta_get_trans(sp, p->namep, ep)) == NULL) {
701 			goto out;
702 		}
703 
704 		if (!(tp->log_error & LDL_ANYERROR)) {
705 			continue;
706 		}
707 
708 		if ((tp->lognamep == NULL) ||
709 		    (strcmp(lognamep->bname, tp->lognamep->bname) != 0)) {
710 			continue;
711 		}
712 
713 		mdclrerror(ep);
714 		is_mounted = (meta_check_inuse(sp,
715 		    p->namep, MDCHK_MOUNTED, ep) != 0);
716 
717 		if (!mdisok(ep) && mdiserror(ep, MDE_IS_MOUNTED)) {
718 			goto out;
719 		}
720 
721 		mdclrerror(ep);
722 		mp = meta_get_mountp(sp, p->namep, ep);
723 
724 		if (!mdisok(ep)) {
725 			goto out;
726 		}
727 
728 		if (is_mounted) {
729 			if (!mp) {
730 				goto out;
731 			}
732 		}
733 
734 		if (is_mounted) {
735 			rc = snprintf(umnt_msg, sizeof (umnt_msg),
736 			    "%s            umount %s\n", umnt_msg, mp);
737 
738 			if (rc < 0) {
739 				goto out;
740 			}
741 		}
742 
743 		rc = snprintf(fsck_msg, sizeof (fsck_msg), "%s %s",
744 		    (any_in_error) ? fsck_msg :
745 		    ((only_fsck) ? "fsck" : "            fsck"),
746 		    p->namep->rname);
747 		if (rc < 0) {
748 			goto out;
749 		}
750 
751 		if (is_mounted) {
752 			rc = snprintf(mnt_msg, sizeof (mnt_msg),
753 			    "%s            mount %s %s\n",
754 			    mnt_msg, p->namep->bname, mp);
755 
756 			if (rc < 0) {
757 				goto out;
758 			}
759 		}
760 
761 		if (mp) {
762 			Free(mp);
763 			mp = NULL;
764 		}
765 
766 		any_in_error |= TRUE;
767 	}
768 
769 	if (!any_in_error) {
770 		goto out;
771 	}
772 
773 	len = strlen(umnt_msg) + strlen(fsck_msg) + strlen(mnt_msg) +
774 							(only_fsck? 1: 0) + 1;
775 	if (!(rmsg = Zalloc(len))) {
776 		len = 0;
777 		goto out;
778 	}
779 	rc = snprintf(rmsg, len, "%s%s%s%s", umnt_msg, fsck_msg,
780 					    !only_fsck? "\n": "", mnt_msg);
781 	if (rc == EOF) {
782 		goto out;
783 	}
784 
785 out:
786 	if (mp) {
787 		Free(mp);
788 		mp = NULL;
789 	}
790 	if (len == 0 && rmsg) {
791 		Free(rmsg);
792 		rmsg = NULL;
793 	}
794 
795 	return (rmsg);
796 }
797 
798 /*
799  * printable log state
800  */
801 char *
802 mt_l_error_to_name(
803 	md_trans_t	*transp,
804 	md_timeval32_t	*tvp,
805 	uint_t		tstate	/* Errored tstate flags */
806 )
807 {
808 	mt_l_error_t	log_error = transp->log_error;
809 
810 	/* grab time */
811 	if (tvp != NULL)
812 		*tvp = transp->log_timestamp;
813 
814 	if (tstate != 0) {
815 		return (dgettext(TEXT_DOMAIN, "Unavailable"));
816 	}
817 
818 	/* return state */
819 	if (log_error & LDL_ERROR) {
820 		return (dgettext(TEXT_DOMAIN, "Error"));
821 	} else if (log_error & LDL_HERROR) {
822 		return (dgettext(TEXT_DOMAIN, "Hard Error"));
823 	} else {
824 		return (dgettext(TEXT_DOMAIN, "Okay"));
825 	}
826 }
827 
828 /*
829  * printable trans state
830  */
831 char *
832 mt_flags_to_name(
833 	md_trans_t	*transp,
834 	md_timeval32_t	*tvp,
835 	uint_t		tstate	/* Errored tstate flags */
836 )
837 {
838 	/* grab time */
839 	if (tvp != NULL)
840 		*tvp = transp->timestamp;
841 
842 	if (tstate != 0) {
843 		return (dgettext(TEXT_DOMAIN, "Unavailable"));
844 	}
845 
846 	/* return state */
847 	if (transp->flags & TRANS_DETACHED)
848 		return (dgettext(TEXT_DOMAIN, "Detached"));
849 	else if (transp->flags & TRANS_DETACHING)
850 		return (dgettext(TEXT_DOMAIN, "Detaching"));
851 	else if (transp->flags & TRANS_ATTACHING)
852 		return (dgettext(TEXT_DOMAIN, "Attaching"));
853 	return (mt_l_error_to_name(transp, tvp, tstate));
854 }
855 
856 /*
857  * report trans
858  */
859 static int
860 trans_report(
861 	mdsetname_t	*sp,
862 	md_trans_t	*transp,
863 	char		*fname,
864 	FILE		*fp,
865 	mdprtopts_t	options,
866 	md_error_t	*ep
867 )
868 {
869 	char		*mt_state;
870 	md_timeval32_t	tv;
871 	char		*timep;
872 	int		rval = -1;
873 	char		*actionp = NULL;
874 	char 		*devid = "";
875 	mdname_t	*didnp = NULL;
876 	ddi_devid_t	dtp;
877 	uint_t		tstate = 0;
878 
879 	/* print header */
880 	if (options & PRINT_HEADER) {
881 		if (fprintf(fp, dgettext(TEXT_DOMAIN, "%s: Trans"
882 		    " (Feature replaced see message below)\n"),
883 		    transp->common.namep->cname) == EOF) {
884 			goto out;
885 		}
886 	}
887 
888 	/* print state */
889 	if (metaismeta(transp->common.namep)) {
890 		if (meta_get_tstate(transp->common.namep->dev, &tstate, ep)
891 		    != 0)
892 			goto out;
893 	}
894 	mt_state = mt_flags_to_name(transp, &tv, tstate & MD_DEV_ERRORED);
895 	if (options & PRINT_TIMES) {
896 		timep = meta_print_time(&tv);
897 	} else {
898 		timep = "";
899 	}
900 	if (fprintf(fp, dgettext(TEXT_DOMAIN, "    State: %-12s %s\n"),
901 	    mt_state, timep) == EOF) {
902 		goto out;
903 	}
904 
905 	if ((tstate & MD_DEV_ERRORED) == 0) {
906 		actionp = mt_flags_to_action(transp);
907 		if (actionp) {
908 			if (fprintf(fp, "%s", actionp) == EOF) {
909 				goto out;
910 			}
911 			Free(actionp);
912 			actionp = NULL;
913 		}
914 	}
915 
916 	/* debug stuff */
917 	if (transp->debug) {
918 		if (fprintf(fp,
919 		    "    Debug Modes:%s%s%s%s%s%s%s%s%s%s%s\n",
920 		    (transp->debug & MT_TRANSACT) ? " TRANSACT" : "",
921 		    (transp->debug & MT_MATAMAP) ? " METADATA" : "",
922 		    (transp->debug & MT_WRITE_CHECK) ?  " WRITES" : "",
923 		    (transp->debug & MT_LOG_WRITE_CHECK) ? " LOGWRITES" : "",
924 		    (transp->debug & MT_CHECK_MAP) ? " MAP" : "",
925 		    (transp->debug & MT_TRACE) ? " TRACE" : "",
926 		    (transp->debug & MT_SIZE) ? " SIZE" : "",
927 		    (transp->debug & MT_NOASYNC) ? " NOASYNC" : "",
928 		    (transp->debug & MT_FORCEROLL) ? " FORCEROLL" : "",
929 		    (transp->debug & MT_SCAN) ? " SCAN" : "",
930 		    (transp->debug & MT_PREWRITE) ? " PREWRITE" : "")
931 		    == EOF) {
932 			goto out;
933 		}
934 	}
935 
936 	/* print size */
937 	if (fprintf(fp, dgettext(TEXT_DOMAIN, "    Size: %lld blocks (%s)\n"),
938 	    transp->common.size,
939 	    meta_number_to_string(transp->common.size, DEV_BSIZE)) == EOF) {
940 		goto out;
941 	}
942 
943 
944 	/* print master */
945 	if (fprintf(fp, dgettext(TEXT_DOMAIN, "    Master Device: %s\n"),
946 	    transp->masternamep->cname) == EOF) {
947 		goto out;
948 	}
949 
950 	/* print log */
951 	if (transp->lognamep != NULL) {
952 		if (fprintf(fp, dgettext(TEXT_DOMAIN,
953 		    "    Logging Device: %s\n"),
954 		    transp->lognamep->cname) == EOF) {
955 			goto out;
956 		}
957 	}
958 
959 	/* add extra line */
960 	if (fprintf(fp, "\n") == EOF)
961 		goto out;
962 
963 	/* print master details if regular device */
964 	if (! metaismeta(transp->masternamep)) {
965 		daddr_t	start_blk = 0;
966 		char	*has_mddb_str = dgettext(TEXT_DOMAIN, "No");
967 		int	len;
968 
969 		/*
970 		 * Building a format string on the fly that will
971 		 * be used in (f)printf. This allows the length
972 		 * of the ctd to vary from small to large without
973 		 * looking horrible.
974 		 */
975 		len = strlen(transp->masternamep->cname) + 2;
976 		len = max(len, strlen(dgettext(TEXT_DOMAIN, "Master Device")));
977 
978 		/* print header */
979 		if (fprintf(fp,
980 		    "\t%-*.*s %-12.12s %-5.5s %s\n",
981 		    len, len,
982 		    dgettext(TEXT_DOMAIN, "Master Device"),
983 		    dgettext(TEXT_DOMAIN, "Start Block"),
984 		    dgettext(TEXT_DOMAIN, "Dbase"),
985 			dgettext(TEXT_DOMAIN, "Reloc")) == EOF) {
986 			goto out;
987 		}
988 
989 		/* populate the key in the name_p structure */
990 		if ((didnp = metadevname(&sp,
991 				transp->masternamep->dev, ep)) == NULL) {
992 			return (-1);
993 		}
994 
995 	    /* determine if devid does NOT exist */
996 		if (options & PRINT_DEVID)
997 		    if ((dtp = meta_getdidbykey(sp->setno, getmyside(sp, ep),
998 					didnp->key, ep)) == NULL)
999 				devid = dgettext(TEXT_DOMAIN, "No ");
1000 			else {
1001 				devid = dgettext(TEXT_DOMAIN, "Yes");
1002 				free(dtp);
1003 			}
1004 
1005 		/* print info */
1006 		/*
1007 		 * This allows the length
1008 		 * of the ctd to vary from small to large without
1009 		 * looking horrible.
1010 		 */
1011 		if (fprintf(fp, "\t%-*s %8ld     %-5.5s %s\n", len,
1012 		    transp->masternamep->cname,
1013 		    start_blk, has_mddb_str, devid) == EOF) {
1014 			goto out;
1015 		}
1016 		/* add extra line */
1017 		if (fprintf(fp, "\n") == EOF)
1018 			goto out;
1019 	}
1020 
1021 	/* success */
1022 	rval = 0;
1023 
1024 	/* cleanup, return error */
1025 out:
1026 	if (rval != 0)
1027 		(void) mdsyserror(ep, errno, fname);
1028 	return (rval);
1029 }
1030 
1031 /*
1032  * print/report trans
1033  */
1034 int
1035 meta_trans_print(
1036 	mdsetname_t	*sp,
1037 	mdname_t	*transnp,
1038 	mdnamelist_t	**nlistpp,
1039 	char		*fname,
1040 	FILE		*fp,
1041 	mdprtopts_t	options,
1042 	int		*meta_print_trans_msgp, /* NULL if transnp != NULL */
1043 	mdnamelist_t	**lognlpp,
1044 	md_error_t	*ep
1045 )
1046 {
1047 	md_trans_t	*transp;
1048 	mdname_t	*lognamep;
1049 
1050 	/* should have same set */
1051 	assert(sp != NULL);
1052 
1053 	/* print all transs */
1054 	if (transnp == NULL) {
1055 		mdnamelist_t	*nlp = NULL;
1056 		mdnamelist_t	*p;
1057 		int		cnt;
1058 		int		rval = 0;
1059 
1060 		/* get list */
1061 		if ((cnt = meta_get_trans_names(sp, &nlp, options, ep)) < 0)
1062 			return (-1);
1063 		else if (cnt == 0)
1064 			return (0);
1065 
1066 		/* recurse */
1067 		for (p = nlp; (p != NULL); p = p->next) {
1068 			mdname_t	*np = p->namep;
1069 
1070 			if (meta_trans_print(sp, np, nlistpp, fname, fp,
1071 			    options, meta_print_trans_msgp, lognlpp, ep) != 0)
1072 				rval = -1;
1073 		}
1074 
1075 		if (meta_print_trans_msgp)
1076 			*meta_print_trans_msgp = 1;
1077 
1078 		/* cleanup, return success */
1079 		metafreenamelist(nlp);
1080 		return (rval);
1081 	}
1082 
1083 
1084 	/* get unit structure */
1085 	if ((transp = meta_get_trans_common(sp, transnp,
1086 	    ((options & PRINT_FAST) ? 1 : 0), ep)) == NULL)
1087 		return (-1);
1088 
1089 	/* save unique log */
1090 	if ((lognlpp != NULL) &&
1091 	    ((lognamep = transp->lognamep) != NULL)) {
1092 		mdnamelist_t	*p;
1093 
1094 		for (p = *lognlpp; (p != NULL); p = p->next) {
1095 			if (strcmp(lognamep->bname, p->namep->bname) == 0)
1096 				break;
1097 		}
1098 		if (p == NULL)
1099 			(void) metanamelist_append(lognlpp, lognamep);
1100 	}
1101 
1102 	/* check for parented */
1103 	if ((! (options & PRINT_SUBDEVS)) &&
1104 	    (MD_HAS_PARENT(transp->common.parent))) {
1105 		return (0);
1106 	}
1107 
1108 	/* can't have a large trans or descriptive name trans */
1109 	if (!(options & (PRINT_LARGEDEVICES | PRINT_FN))) {
1110 		/* print appropriate detail */
1111 		if (options & PRINT_SHORT) {
1112 			if (trans_print(transp, fname, fp, ep) != 0)
1113 				return (-1);
1114 		} else {
1115 			if (trans_report(sp, transp, fname, fp, options, ep)
1116 			    != 0)
1117 				return (-1);
1118 		}
1119 	}
1120 
1121 	/* print underlying metadevices, log is later */
1122 	if (metaismeta(transp->masternamep)) {
1123 		if (meta_print_name(sp, transp->masternamep, nlistpp, fname,
1124 		    fp, (options | PRINT_HEADER | PRINT_SUBDEVS), NULL, ep)
1125 		    != 0) {
1126 			return (-1);
1127 		}
1128 	}
1129 
1130 	/* return success */
1131 	return (0);
1132 }
1133 
1134 /*
1135  * print log
1136  */
1137 static int
1138 log_print(
1139 	mdsetname_t	*sp,
1140 	mdname_t	*lognamep,
1141 	char		*fname,
1142 	FILE		*fp,
1143 	mdprtopts_t	options,
1144 	md_error_t	*ep
1145 )
1146 {
1147 	mdnamelist_t	*nlp = NULL;
1148 
1149 	/* metadevice info */
1150 	if (metaismeta(lognamep)) {
1151 		return (meta_print_name(sp, lognamep, &nlp, fname, fp,
1152 		    options, NULL, ep));
1153 	}
1154 
1155 	/* regular device info */
1156 	return (0);
1157 }
1158 
1159 /*
1160  * report log
1161  */
1162 static int
1163 log_report(
1164 	mdsetname_t	*sp,
1165 	mdname_t	*lognamep,
1166 	mdnamelist_t	**nlistpp,
1167 	char		*fname,
1168 	FILE		*fp,
1169 	mdprtopts_t	options,
1170 	mdnamelist_t	*transnlp,
1171 	md_error_t	*ep
1172 )
1173 {
1174 	md_trans_t	*transp = NULL;
1175 	mdnamelist_t	*p;
1176 	char		*ml_state;
1177 	md_timeval32_t	tv;
1178 	char		*timep;
1179 	char		*actionp = NULL;
1180 	int		rval = -1;
1181 	char		*devid = " ";
1182 	mdname_t	*didnp = NULL;
1183 	ddi_devid_t	dtp;
1184 	uint_t		tstate = 0;
1185 
1186 	for (p = transnlp; (p != NULL); p = p->next) {
1187 		md_trans_t	*tp;
1188 
1189 		if ((tp = meta_get_trans(sp, p->namep, ep)) == NULL)
1190 			return (-1);
1191 		if ((tp->lognamep != NULL) &&
1192 		    (strcmp(lognamep->bname, tp->lognamep->bname) == 0)) {
1193 			transp = tp;	/* save any parent trans */
1194 		}
1195 	}
1196 
1197 	/* we must have at least one trans */
1198 	assert(transp != NULL);
1199 	if (transp == NULL) {
1200 		rval = 0;
1201 		goto out;
1202 	}
1203 
1204 	if ((options & PRINT_LARGEDEVICES) &&
1205 	    (transp->log_size <= MD_MAX_BLKS_FOR_SMALL_DEVS)) {
1206 		rval = 0;
1207 		goto out;
1208 	}
1209 
1210 	/* print header and trans devices, collect log_error and size */
1211 	if (fprintf(fp, dgettext(TEXT_DOMAIN, "%s: Logging device for"),
1212 	    lognamep->cname) == EOF) {
1213 		goto out;
1214 	}
1215 
1216 	if ((transp->lognamep != NULL) &&
1217 	    (strcmp(lognamep->bname, transp->lognamep->bname) == 0)) {
1218 		if (fprintf(fp, " %s", transp->common.namep->cname)
1219 		    == EOF) {
1220 			goto out;
1221 		}
1222 	}
1223 	if (fprintf(fp, "\n") == EOF)
1224 		goto out;
1225 
1226 	/* print state */
1227 	if (metaismeta(transp->lognamep)) {
1228 		if (meta_get_tstate(transp->lognamep->dev, &tstate, ep) != 0)
1229 			return (-1);
1230 	}
1231 	ml_state = mt_l_error_to_name(transp, &tv, tstate & MD_DEV_ERRORED);
1232 	if (options & PRINT_TIMES) {
1233 		timep = meta_print_time(&tv);
1234 	} else {
1235 		timep = "";
1236 	}
1237 	if (fprintf(fp, dgettext(TEXT_DOMAIN, "    State: %-12s %s\n"),
1238 	    ml_state, timep) == EOF) {
1239 		goto out;
1240 	}
1241 
1242 	if ((tstate & MD_DEV_ERRORED) == 0) {
1243 		actionp = mt_l_error_to_action(sp, transnlp, lognamep, ep);
1244 		if (actionp) {
1245 			if (fprintf(fp, dgettext(TEXT_DOMAIN,
1246 			    "    Invoke: %s\n"), actionp) == EOF) {
1247 				goto out;
1248 			}
1249 			Free(actionp);
1250 			actionp = NULL;
1251 		}
1252 	}
1253 
1254 	/* print size */
1255 	if (fprintf(fp, dgettext(TEXT_DOMAIN, "    Size: %ld blocks (%s)\n"),
1256 	    transp->log_size,
1257 	    meta_number_to_string(transp->log_size, DEV_BSIZE)) == EOF) {
1258 		goto out;
1259 	}
1260 
1261 	/* MD_DEBUG stuff */
1262 	if (options & PRINT_DEBUG) {
1263 		mdname_t	*transnp = transp->common.namep;
1264 		mt_unit_t	*mt;
1265 		daddr_t		blksinuse, head, tail, nblks, eblk, sblk;
1266 		int		percent;
1267 
1268 		if ((mt = (mt_unit_t *)meta_get_mdunit(sp, transnp, ep))
1269 		    == NULL) {
1270 			return (-1);
1271 		}
1272 		assert(mt->c.un_type == MD_METATRANS);
1273 
1274 		if (fprintf(fp, dgettext(TEXT_DOMAIN,
1275 		    "    Transfer Size: %d blocks\n"),
1276 		    mt->un_l_maxtransfer) == EOF) {
1277 			Free(mt);
1278 			goto out;
1279 		}
1280 
1281 		head = mt->un_l_head;
1282 		tail = mt->un_l_tail;
1283 		sblk = mt->un_l_sblk;
1284 		nblks = mt->un_l_nblks;
1285 		eblk = sblk + nblks;
1286 		if (head <= tail)
1287 			blksinuse = tail - head;
1288 		else
1289 			blksinuse = (eblk - head) + (tail - sblk);
1290 
1291 		percent = ((u_longlong_t)blksinuse * 100) / nblks;
1292 		if (fprintf(fp, dgettext(TEXT_DOMAIN,
1293 		    "    Full: %d%% (%ld of %ld blocks)\n"),
1294 		    percent, blksinuse, nblks) == EOF) {
1295 			Free(mt);
1296 			goto out;
1297 		}
1298 
1299 		percent = ((u_longlong_t)mt->un_l_resv * 100) /
1300 		    mt->un_l_maxresv;
1301 		if (fprintf(fp, dgettext(TEXT_DOMAIN,
1302 		    "    Reserved: %d%% (%ud of %ud bytes)\n"),
1303 		    percent, mt->un_l_resv, mt->un_l_maxresv) == EOF) {
1304 			Free(mt);
1305 			goto out;
1306 		}
1307 		Free(mt);
1308 	}
1309 
1310 	/* add extra line */
1311 	if (fprintf(fp, "\n") == EOF)
1312 		goto out;
1313 
1314 	/* print log details */
1315 	if (metaismeta(lognamep)) {
1316 		if (meta_print_name(sp, lognamep, nlistpp, fname, fp,
1317 		    options, NULL, ep) != 0) {
1318 			return (-1);
1319 		}
1320 	} else {
1321 		daddr_t		start_blk;
1322 		int		has_mddb;
1323 		char		*has_mddb_str;
1324 		int		len;
1325 
1326 		/*
1327 		 * Building a format string on the fly that will
1328 		 * be used in (f)printf. This allows the length
1329 		 * of the ctd to vary from small to large without
1330 		 * looking horrible.
1331 		 */
1332 		len = strlen(lognamep->cname) + 2;
1333 		len = max(len, strlen(dgettext(TEXT_DOMAIN, "Logging Device")));
1334 		/* print header */
1335 		if (fprintf(fp,
1336 		    "\t%-*.*s %-12.12s %-5.5s %s\n",
1337 		    len, len,
1338 		    dgettext(TEXT_DOMAIN, "Logging Device"),
1339 		    dgettext(TEXT_DOMAIN, "Start Block"),
1340 		    dgettext(TEXT_DOMAIN, "Dbase"),
1341 			dgettext(TEXT_DOMAIN, "Reloc")) == EOF) {
1342 			goto out;
1343 		}
1344 		/* get info */
1345 		if ((start_blk = metagetstart(sp, lognamep, ep)) ==
1346 		    MD_DISKADDR_ERROR) {
1347 			return (-1);
1348 		}
1349 		if ((has_mddb = metahasmddb(sp, lognamep, ep)) < 0) {
1350 			return (-1);
1351 		}
1352 		if (has_mddb)
1353 			has_mddb_str = dgettext(TEXT_DOMAIN, "Yes");
1354 		else
1355 			has_mddb_str = dgettext(TEXT_DOMAIN, "No");
1356 
1357 		/* populate the key in the name_p structure */
1358 		if ((didnp = metadevname(&sp, lognamep->dev, ep)) == NULL) {
1359 			return (-1);
1360 		}
1361 
1362 	    /* determine if devid does NOT exist */
1363 		if (options & PRINT_DEVID)
1364 		    if ((dtp = meta_getdidbykey(sp->setno, getmyside(sp, ep),
1365 					didnp->key, ep)) == NULL)
1366 				devid = dgettext(TEXT_DOMAIN, "No ");
1367 			else {
1368 				devid = dgettext(TEXT_DOMAIN, "Yes");
1369 				free(dtp);
1370 			}
1371 
1372 		/* print info */
1373 		/*
1374 		 * This allows the length
1375 		 * of the ctd to vary from small to large without
1376 		 * looking horrible.
1377 		 */
1378 		if (fprintf(fp, "\t%-*s %8ld     %-5.5s %s\n",
1379 		    len, lognamep->cname, start_blk,
1380 		    has_mddb_str, devid) == EOF) {
1381 			goto out;
1382 		}
1383 	}
1384 
1385 	/* add extra line */
1386 	if (fprintf(fp, "\n") == EOF)
1387 		goto out;
1388 
1389 	/* success */
1390 	rval = 0;
1391 
1392 	/* cleanup, return error */
1393 out:
1394 	if (rval != 0)
1395 		(void) mdsyserror(ep, errno, fname);
1396 	return (rval);
1397 }
1398 
1399 /*
1400  * print/report logs
1401  */
1402 int
1403 meta_logs_print(
1404 	mdsetname_t	*sp,
1405 	mdnamelist_t	*lognlp,
1406 	mdnamelist_t	**nlistpp,
1407 	char		*fname,
1408 	FILE		*fp,
1409 	mdprtopts_t	options,
1410 	md_error_t	*ep
1411 )
1412 {
1413 	mdnamelist_t	*transnlp = NULL;
1414 	mdnamelist_t	*p;
1415 	int		rval = 0;
1416 
1417 	/* must have a set */
1418 	assert(sp != NULL);
1419 
1420 	/* get trans devices */
1421 	if (lognlp == NULL)
1422 		return (0);
1423 
1424 	if (! (options & PRINT_SHORT))
1425 		if (meta_get_trans_names(sp, &transnlp, options, ep) < 0)
1426 			return (-1);
1427 
1428 	/* print all logs */
1429 	options |= PRINT_SUBDEVS;
1430 	for (p = lognlp; (p != NULL); p = p->next) {
1431 		mdname_t	*lognamep = p->namep;
1432 
1433 		/* print appropriate detail */
1434 		if (options & PRINT_SHORT) {
1435 			if (log_print(sp, lognamep, fname, fp, options,
1436 			    ep) != 0) {
1437 				rval = -1;
1438 			}
1439 		} else {
1440 			if (log_report(sp, lognamep, nlistpp, fname, fp,
1441 			    options, transnlp, ep) != 0) {
1442 				rval = -1;
1443 			}
1444 		}
1445 	}
1446 
1447 	/* cleanup, return success */
1448 out:
1449 	metafreenamelist(transnlp);
1450 	return (rval);
1451 }
1452 
1453 /*
1454  * meta_lockfs_common -- common lock and unlock code
1455  *
1456  * Normally this routine will return a 0 for success. Even if
1457  * lockfs wasn't able to lock down the filesystem. The reason
1458  * for this is that the master device can be in an errored state
1459  * and the lock can't be obtained. We don't want to prevent
1460  * possible recovery in this case and it's not likely any activity
1461  * will be occurring. If the filesystem is healthy with activity
1462  * lockfs will successfully lock the filesystem and return an
1463  * error code of 0.
1464  *
1465  * The one case where this routine returns a non-zero value would
1466  * be if we can't determine the outcome of the lockfs. This should
1467  * never occur because we don't catch signals that could cause
1468  * waitpid() to prematurely return.
1469  */
1470 static int
1471 meta_lockfs_common(mdname_t *fs, void **cookie, int lockit)
1472 {
1473 	char		*blkname;
1474 	FILE		*m;
1475 	struct mnttab	tab_wildcard, tab_match;
1476 	pid_t		pid;
1477 	int		lock_exit;
1478 
1479 	(void) memset(&tab_wildcard, 0, sizeof (tab_wildcard));
1480 	(void) memset(&tab_match, 0, sizeof (tab_match));
1481 
1482 	if ((blkname = fs->bname) == NULL)
1483 		blkname = getfullblkname(fs->cname);
1484 
1485 	tab_wildcard.mnt_special = blkname;
1486 
1487 	if ((m = fopen(MNTTAB, "r")) == NULL) {
1488 		/*
1489 		 * No mnttab means nothing is mounted
1490 		 */
1491 		*cookie = 0;
1492 		return (0);
1493 	}
1494 
1495 	if (getmntany(m, &tab_match, &tab_wildcard)) {
1496 		/*
1497 		 * No match in mnttab so we're not mounted ... at least
1498 		 * nothing better be mounted.
1499 		 */
1500 		*cookie = 0;
1501 		return (0);
1502 	}
1503 
1504 	(void) fclose(m);
1505 
1506 	switch (pid = fork()) {
1507 	    case -1:
1508 		/*
1509 		 * We've got some major trouble here and shouldn't
1510 		 * continue. The user needs to clear up the problems
1511 		 * that the system currently has before proceeding
1512 		 * to detach the log.
1513 		 */
1514 		(void) printf(dgettext(TEXT_DOMAIN, "failed to fork lockfs\n"));
1515 		*cookie = 0;
1516 		return (1);
1517 
1518 	    case 0:
1519 		(void) execl("/usr/sbin/lockfs", "lockfs", lockit ? "-w" : "-u",
1520 		    "-c", "Solaris Volume Manager detach lock",
1521 		    tab_match.mnt_mountp, 0);
1522 		/*
1523 		 * Shouldn't reach here, but if this code is run on
1524 		 * a release that doesn't have lockfs return an error
1525 		 * code so that the -f (force) option could be used
1526 		 * by metadetach.
1527 		 */
1528 		exit(1);
1529 
1530 	    default:
1531 		if (waitpid(pid, &lock_exit, 0) != pid) {
1532 			/*
1533 			 * We couldn't get status regarding the
1534 			 * outcome of the lockfs command. We should
1535 			 * attempt to unlock the filesystem though.
1536 			 * Return an error code so that if the user
1537 			 * is trying to force the detach make them
1538 			 * clear up this problem first.
1539 			 */
1540 			*cookie = (void *)1;
1541 			return (1);
1542 		}
1543 
1544 		*cookie = (void *)1;
1545 		return (0);
1546 	}
1547 }
1548 
1549 /*
1550  * meta_lockfs - if mounted, lock a given device against writes
1551  *
1552  * See comment section for meta_lockfs_common
1553  */
1554 static int
1555 meta_lockfs(mdname_t *fs, void **cookie)
1556 {
1557 	return (meta_lockfs_common(fs, cookie, 1));
1558 }
1559 
1560 /*
1561  * meta_unlockfs - if mounted, unlock the filesystem if previously locked
1562  *
1563  * See comment section for meta_lockfs_common
1564  */
1565 static void
1566 meta_unlockfs(mdname_t *fs, void **cookie)
1567 {
1568 	/*
1569 	 * Simple time saver. We could always try to unlock
1570 	 * the filesystem, that takes time a resources.
1571 	 */
1572 	if (*cookie == (void *)1)
1573 		(void) meta_lockfs_common(fs, cookie, 0);
1574 }
1575 
1576 /*
1577  * meta_trans_detach -- detach log from trans device
1578  */
1579 int
1580 meta_trans_detach(
1581 	mdsetname_t	*sp,
1582 	mdname_t	*transnp,
1583 	mdcmdopts_t	options,
1584 	int		*delayed,
1585 	md_error_t	*ep
1586 )
1587 {
1588 	int		force = ((options & MDCMD_FORCE) ? 1 : 0);
1589 	md_i_get_t	detach;
1590 	md_trans_t	*transp;
1591 	mdname_t	*lognp;
1592 	void		*lock_cookie;
1593 
1594 	/* should have a set */
1595 	assert(sp != NULL);
1596 	assert(sp->setno == MD_MIN2SET(meta_getminor(transnp->dev)));
1597 
1598 	/* check name */
1599 	if (metachkmeta(transnp, ep) != 0)
1600 		return (-1);
1601 
1602 	/* save log name */
1603 	if ((transp = meta_get_trans(sp, transnp, ep)) == NULL)
1604 		return (-1);
1605 	if ((lognp = transp->lognamep) == NULL)
1606 		return (mdmderror(ep, MDE_NO_LOG, meta_getminor(transnp->dev),
1607 		    transnp->cname));
1608 
1609 	/*
1610 	 * If trans device is mounted lock the filesystem
1611 	 * against writes and mod time updates.
1612 	 */
1613 	if (force && meta_lockfs(transnp, &lock_cookie)) {
1614 		/*
1615 		 * This device is mounted and we were unable
1616 		 * lock the device. Data corruption can occur
1617 		 * if we don't lock the device before removing
1618 		 * the log so bail out here.
1619 		 * NOTE: There's one case were the exist status
1620 		 * of lockfs could have been lost yet the command
1621 		 * could have run. We should try to unlock the filesystem
1622 		 * before returning.
1623 		 */
1624 		meta_unlockfs(transnp, &lock_cookie);
1625 		return (mdmderror(ep, MDE_UNKNOWN_TYPE,
1626 		    meta_getminor(transnp->dev), transnp->cname));
1627 	}
1628 
1629 	/* detach log */
1630 	*delayed = 0;
1631 	(void) memset(&detach, 0, sizeof (detach));
1632 	detach.id = meta_getminor(transnp->dev);
1633 	MD_SETDRIVERNAME(&detach, MD_TRANS, sp->setno);
1634 	detach.size = force;
1635 	if (metaioctl(MD_IOC_TRANS_DETACH, &detach, &detach.mde, NULL) != 0) {
1636 		/* delayed detach */
1637 		if ((force) && (mdissyserror(&detach.mde, EBUSY))) {
1638 			*delayed = 1;
1639 			mdclrerror(&detach.mde);
1640 		} else {
1641 			meta_unlockfs(transnp, &lock_cookie);
1642 			return (mdstealerror(ep, &detach.mde));
1643 		}
1644 	}
1645 
1646 	/*
1647 	 * Unlock the filesystem
1648 	 */
1649 	meta_unlockfs(transnp, &lock_cookie);
1650 
1651 	/* clear cache */
1652 	meta_invalidate_name(lognp);
1653 	meta_invalidate_name(transnp);
1654 
1655 	/* let em know */
1656 	if (options & MDCMD_PRINT) {
1657 		if (*delayed) {
1658 			(void) printf(dgettext(TEXT_DOMAIN,
1659 "%s: logging device %s will be detached at unmount or reboot\n"),
1660 			    transnp->cname, lognp->cname);
1661 		} else {
1662 			(void) printf(dgettext(TEXT_DOMAIN,
1663 			    "%s: logging device %s is detached\n"),
1664 			    transnp->cname, lognp->cname);
1665 		}
1666 		(void) fflush(stdout);
1667 	}
1668 
1669 	/* return success */
1670 	return (0);
1671 }
1672 
1673 /*
1674  * reset trans
1675  */
1676 int
1677 meta_trans_reset(
1678 	mdsetname_t	*sp,
1679 	mdname_t	*transnp,
1680 	mdcmdopts_t	options,
1681 	md_error_t	*ep
1682 )
1683 {
1684 	md_trans_t	*transp;
1685 	int		rval = -1;
1686 
1687 	/* should have a set */
1688 	assert(sp != NULL);
1689 	assert((transnp == NULL) ||
1690 	    (sp->setno == MD_MIN2SET(meta_getminor(transnp->dev))));
1691 
1692 	/* reset all trans */
1693 	if (transnp == NULL) {
1694 		mdnamelist_t	*transnlp = NULL;
1695 		mdnamelist_t	*p;
1696 
1697 		/* for each trans */
1698 		rval = 0;
1699 		if (meta_get_trans_names(sp, &transnlp, 0, ep) < 0)
1700 			return (-1);
1701 		for (p = transnlp; (p != NULL); p = p->next) {
1702 			/* reset trans */
1703 			transnp = p->namep;
1704 			if (meta_trans_reset(sp, transnp, options, ep) != 0) {
1705 				rval = -1;
1706 				break;
1707 			}
1708 		}
1709 
1710 		/* cleanup, return success */
1711 		metafreenamelist(transnlp);
1712 		return (rval);
1713 	}
1714 
1715 	/* check name */
1716 	if (metachkmeta(transnp, ep) != 0)
1717 		return (-1);
1718 	/* get unit structure */
1719 	if ((transp = meta_get_trans(sp, transnp, ep)) == NULL)
1720 		return (-1);
1721 
1722 	/* make sure nobody owns us */
1723 	if (MD_HAS_PARENT(transp->common.parent)) {
1724 		return (mdmderror(ep, MDE_IN_USE, meta_getminor(transnp->dev),
1725 		    transnp->cname));
1726 	}
1727 
1728 	/* clear subdevices cache */
1729 	meta_invalidate_name(transp->masternamep);
1730 	if (transp->lognamep)
1731 		meta_invalidate_name(transp->lognamep);
1732 
1733 	/* clear metadevice */
1734 	if (meta_reset(sp, transnp, options, ep) != 0)
1735 		goto out;
1736 	rval = 0;	/* success */
1737 
1738 	/* let em know */
1739 	if (options & MDCMD_PRINT) {
1740 		(void) printf(dgettext(TEXT_DOMAIN, "%s: Trans is cleared\n"),
1741 		    transnp->cname);
1742 		(void) fflush(stdout);
1743 	}
1744 
1745 	/* clear subdevices */
1746 	if (! (options & MDCMD_RECURSE))
1747 		goto out;
1748 	if (metaismeta(transp->masternamep)) {
1749 		mdname_t	*masternp = transp->masternamep;
1750 
1751 		if (meta_reset_by_name(sp, masternp, options, ep) != 0)
1752 			rval = -1;
1753 	}
1754 	/* (multi-parented) log will be cleared later */
1755 
1756 	/* cleanup, return success */
1757 out:
1758 	meta_invalidate_name(transnp);
1759 	return (rval);
1760 }
1761