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