xref: /titanic_41/usr/src/cmd/lvm/util/metahs.c (revision d7cd82522afdd890a66c7600b499590ad44e84bd)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * hotspare maintenance
30  */
31 
32 #include <meta.h>
33 #include <sdssc.h>
34 
35 /*
36  * possible actions
37  */
38 enum metahs_op {
39 	NONE,
40 	ADD_A_HS,
41 	DELETE_A_HS,
42 	ENABLE_A_HS,
43 	REPLACE_A_HS,
44 	STATUS_A_HSP
45 };
46 
47 /*
48  * report status of a hotspare pool
49  */
50 static int
status_hsp(mdsetname_t * sp,mdhspname_t * hspnp,md_error_t * ep)51 status_hsp(
52 	mdsetname_t	*sp,
53 	mdhspname_t	*hspnp,
54 	md_error_t	*ep
55 )
56 {
57 	mdprtopts_t	options = (PRINT_HEADER | PRINT_SUBDEVS | PRINT_DEVID);
58 	mdnamelist_t	*nlp = NULL;
59 
60 	/* must have set */
61 	assert(sp != NULL);
62 	assert(hspnp->hsp == MD_HSP_NONE || sp->setno == HSP_SET(hspnp->hsp));
63 
64 	/* print status */
65 	if (meta_hsp_print(sp, hspnp, &nlp, NULL, stdout, options, ep) != 0)
66 		return (-1);
67 
68 	/* return success */
69 	return (0);
70 }
71 
72 /*
73  * print usage message
74  */
75 static void
usage(mdsetname_t * sp,int eval)76 usage(
77 	mdsetname_t	*sp,
78 	int		eval
79 )
80 {
81 	(void) fprintf(stderr, gettext("\
82 usage:	%s [-s setname] -a hot_spare_pool [component...]\n\
83 	%s [-s setname] -a \"all\" component...\n\
84 	%s [-s setname] -d hot_spare_pool [component...]\n\
85 	%s [-s setname] -d \"all\" component...\n\
86 	%s [-s setname] -e component...\n\
87 	%s [-s setname] -r hot_spare_pool component_old component_new\n\
88 	%s [-s setname] -r \"all\" component_old component_new\n\
89 	%s [-s setname] -i [hot_spare_pool...]\n"),
90 	    myname, myname, myname, myname, myname, myname, myname, myname);
91 	md_exit(sp, eval);
92 }
93 
94 /*
95  * parse args and add hotspares
96  */
97 static int
add_hotspares(mdsetname_t ** spp,int argc,char * argv[],mdcmdopts_t options,md_error_t * ep)98 add_hotspares(
99 	mdsetname_t	**spp,
100 	int		argc,
101 	char		*argv[],
102 	mdcmdopts_t	options,
103 	md_error_t	*ep
104 )
105 {
106 	mdhspnamelist_t	*hspnlp = NULL;
107 	mdnamelist_t	*nlp = NULL;
108 	int		cnt;
109 	mdhspnamelist_t	*p;
110 	int		rval = -1;
111 
112 	/* get hotspare pool name(s) */
113 	if (argc < 1)
114 		usage(*spp, 1);
115 	if ((argc > 1) && meta_is_all(argv[0])) {
116 		/* check for ownership */
117 		assert(*spp != NULL);
118 		if (meta_check_ownership(*spp, ep) != 0)
119 			return (-1);
120 
121 		if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) {
122 			return (-1);
123 		} else if (cnt == 0) {
124 			return (mderror(ep, MDE_NO_HSPS, NULL));
125 		}
126 	} else { /* create the hsp nmlist from the specified hsp name */
127 		if (!is_hspname(argv[0]))
128 			return (mderror(ep, MDE_NAME_ILLEGAL, argv[0]));
129 
130 		if ((cnt = metahspnamelist(spp, &hspnlp, 1, &argv[0], ep)) < 0)
131 			return (-1);
132 	}
133 	assert(cnt > 0);
134 	--argc, ++argv;
135 
136 	assert(*spp != NULL);
137 
138 	/* grab set lock */
139 	if (meta_lock(*spp, TRUE, ep))
140 		return (-1);
141 
142 	/* check for ownership */
143 	if (meta_check_ownership(*spp, ep) != 0)
144 		return (-1);
145 
146 	/* get hotspares */
147 	if (metanamelist(spp, &nlp, argc, argv,
148 	    LOGICAL_DEVICE, ep) < 0) {
149 		goto out;
150 	}
151 
152 	/* add hotspares */
153 	for (p = hspnlp; (p != NULL); p = p->next) {
154 		mdhspname_t	*hspnp = p->hspnamep;
155 
156 		if (meta_hs_add(*spp, hspnp, nlp, options, ep) != 0)
157 			goto out;
158 	}
159 	rval = 0;
160 
161 	/* cleanup, return success */
162 out:
163 	if (hspnlp != NULL)
164 		metafreehspnamelist(hspnlp);
165 	if (nlp != NULL)
166 		metafreenamelist(nlp);
167 	return (rval);
168 }
169 
170 /*
171  * parse args and delete hotspares
172  */
173 static int
delete_hotspares(mdsetname_t ** spp,int argc,char * argv[],mdcmdopts_t options,md_error_t * ep)174 delete_hotspares(
175 	mdsetname_t	**spp,
176 	int		argc,
177 	char		*argv[],
178 	mdcmdopts_t	options,
179 	md_error_t	*ep
180 )
181 {
182 	mdhspnamelist_t	*hspnlp = NULL;
183 	mdnamelist_t	*nlp = NULL;
184 	int		cnt;
185 	mdhspnamelist_t	*p;
186 	int		rval = -1;
187 
188 	/* get hotspare pool name(s) */
189 	if (argc < 1)
190 		usage(*spp, 1);
191 	if ((argc > 1) && meta_is_all(argv[0])) {
192 		/* check for ownership */
193 		assert(*spp != NULL);
194 		if (meta_check_ownership(*spp, ep) != 0)
195 			return (-1);
196 
197 		if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) {
198 			return (-1);
199 		} else if (cnt == 0) {
200 			return (mderror(ep, MDE_NO_HSPS, NULL));
201 		}
202 	} else if ((cnt = metahspnamelist(spp, &hspnlp, 1, &argv[0],
203 	    ep)) < 0) {
204 		return (-1);
205 	}
206 	assert(cnt > 0);
207 	--argc, ++argv;
208 
209 	assert(*spp != NULL);
210 
211 	/* grab set lock */
212 	if (meta_lock(*spp, TRUE, ep))
213 		return (-1);
214 
215 	/* check for ownership */
216 	if (meta_check_ownership(*spp, ep) != 0)
217 		return (-1);
218 
219 	/* get hotspares */
220 	if (metanamelist(spp, &nlp, argc, argv,
221 	    LOGICAL_DEVICE, ep) < 0) {
222 		goto out;
223 	}
224 
225 	/* delete hotspares */
226 	cnt = 0;
227 	for (p = hspnlp; (p != NULL); p = p->next) {
228 		mdhspname_t	*hspnp = p->hspnamep;
229 
230 		if (meta_hs_delete(*spp, hspnp, nlp, options, ep) != 0) {
231 			if (mdisdeverror(ep, MDE_INVAL_HS))
232 				mdclrerror(ep);
233 			else
234 				goto out;
235 		} else {
236 			++cnt;
237 		}
238 	}
239 
240 	/* make sure we got some */
241 	if ((nlp != NULL) && (cnt == 0)) {
242 		(void) mddeverror(ep, MDE_INVAL_HS, nlp->namep->dev,
243 		    nlp->namep->cname);
244 		goto out;
245 	}
246 
247 	/* success */
248 	rval = 0;
249 
250 	/* cleanup, return success */
251 out:
252 	if (hspnlp != NULL)
253 		metafreehspnamelist(hspnlp);
254 	if (nlp != NULL)
255 		metafreenamelist(nlp);
256 	return (rval);
257 }
258 
259 /*
260  * parse args and enable hotspares
261  */
262 static int
enable_hotspares(mdsetname_t ** spp,int argc,char * argv[],mdcmdopts_t options,md_error_t * ep)263 enable_hotspares(
264 	mdsetname_t	**spp,
265 	int		argc,
266 	char		*argv[],
267 	mdcmdopts_t	options,
268 	md_error_t	*ep
269 )
270 {
271 	mdnamelist_t	*nlp = NULL;
272 	int		rval = -1;
273 
274 	/* enable hotspares */
275 	if (argc < 1)
276 		usage(*spp, 1);
277 
278 	/* get list of hotspares */
279 	if (metanamelist(spp, &nlp, argc, argv,
280 	    LOGICAL_DEVICE, ep) < 0)
281 		goto out;
282 	assert(nlp != NULL);
283 
284 	assert(*spp != NULL);
285 
286 	/* grab set lock */
287 	if (meta_lock(*spp, TRUE, ep))
288 		return (-1);
289 
290 	/* check for ownership */
291 	if (meta_check_ownership(*spp, ep) != 0)
292 		return (-1);
293 
294 	/* enable hotspares */
295 	rval = meta_hs_enable(*spp, nlp, options, ep);
296 
297 	/* cleanup, return success */
298 out:
299 	metafreenamelist(nlp);
300 	return (rval);
301 }
302 
303 /*
304  * parse args and replace hotspares
305  */
306 static int
replace_hotspares(mdsetname_t ** spp,int argc,char * argv[],mdcmdopts_t options,md_error_t * ep)307 replace_hotspares(
308 	mdsetname_t	**spp,
309 	int		argc,
310 	char		*argv[],
311 	mdcmdopts_t	options,
312 	md_error_t	*ep
313 )
314 {
315 	mdhspnamelist_t	*hspnlp = NULL;
316 	int		cnt;
317 	mdname_t	*oldnp;
318 	mdname_t	*newnp;
319 	mdhspnamelist_t	*p;
320 	int		rval = -1;
321 
322 	/* get hotspare pool name(s) */
323 	if (argc != 3)
324 		usage(*spp, 1);
325 	if (meta_is_all(argv[0])) {
326 		/* check for ownership */
327 		assert(*spp != NULL);
328 		if (meta_check_ownership(*spp, ep) != 0)
329 			return (-1);
330 
331 		if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) {
332 			return (-1);
333 		} else if (cnt == 0) {
334 			return (mderror(ep, MDE_NO_HSPS, NULL));
335 		}
336 	} else if ((cnt = metahspnamelist(spp, &hspnlp, 1, &argv[0],
337 	    ep)) < 0) {
338 		return (-1);
339 	}
340 	assert(cnt > 0);
341 
342 	assert(*spp != NULL);
343 
344 	/* grab set lock */
345 	if (meta_lock(*spp, TRUE, ep))
346 		return (-1);
347 
348 	/* check for ownership */
349 	if (meta_check_ownership(*spp, ep) != 0)
350 		return (-1);
351 
352 	/* get old component */
353 	if ((oldnp = metaname(spp, argv[1], LOGICAL_DEVICE, ep)) == NULL)
354 		goto out;
355 
356 	/* get new component */
357 	if ((newnp = metaname(spp, argv[2], LOGICAL_DEVICE, ep)) == NULL)
358 		goto out;
359 
360 	/* replace hotspares */
361 	cnt = 0;
362 	for (p = hspnlp; (p != NULL); p = p->next) {
363 		mdhspname_t	*hspnp = p->hspnamep;
364 
365 		if (meta_hs_replace(*spp, hspnp, oldnp, newnp, options, ep)
366 		    != 0) {
367 			if (mdisdeverror(ep, MDE_INVAL_HS))
368 				mdclrerror(ep);
369 			else
370 				goto out;
371 		} else {
372 			++cnt;
373 		}
374 	}
375 
376 	/* make sure we got some */
377 	if (cnt == 0) {
378 		(void) mddeverror(ep, MDE_INVAL_HS, oldnp->dev, oldnp->cname);
379 		goto out;
380 	}
381 
382 	/* success */
383 	rval = 0;
384 
385 	/* cleanup, return success */
386 out:
387 	if (hspnlp != NULL)
388 		metafreehspnamelist(hspnlp);
389 	return (rval);
390 }
391 
392 /*
393  * print_hsp_devid will collect the information for each underlying
394  * physical device for all the hotspare pools and print out the
395  * device relocation information
396  * INPUT:
397  *	mdsetname_t *sp			set the hsp is in
398  *	mdhspnamelist_t *hspnlp		list of hsp
399  *	FILE	*fp			where to print to
400  *	md_error_t	*ep		errors
401  * RETURN:
402  *	0 	SUCCESS
403  *	-1	ERROR
404  */
405 static int
print_hsp_devid(mdsetname_t * sp,mdhspnamelist_t * hspnlp,FILE * fp,md_error_t * ep)406 print_hsp_devid(
407 	mdsetname_t	*sp,
408 	mdhspnamelist_t *hspnlp,
409 	FILE		*fp,
410 	md_error_t	*ep
411 )
412 {
413 	mddevid_t	*ldevidp = NULL;
414 	int		retval = 0;
415 	mdhspnamelist_t	*p;
416 	mddevid_t	*nextp;
417 
418 	/* for all hotspare pools */
419 	for (p = hspnlp; (p != NULL); p = p->next) {
420 		mdhspname_t	*hspnp = p->hspnamep;
421 		uint_t		hsi;
422 
423 		/* for all hotspares within a pool */
424 		for (hsi = 0;
425 		    hsi < hspnp->unitp->hotspares.hotspares_len; hsi++) {
426 			mdname_t	*hsname;
427 
428 			hsname =
429 			    hspnp->unitp->hotspares.hotspares_val[hsi].hsnamep;
430 
431 			meta_create_non_dup_list(hsname, &ldevidp);
432 		}
433 	}
434 
435 	retval = meta_print_devid(sp, fp, ldevidp, ep);
436 
437 	/* cleanup */
438 	for (nextp = ldevidp; nextp != NULL; ldevidp = nextp) {
439 		Free(ldevidp->ctdname);
440 		nextp = ldevidp->next;
441 		Free(ldevidp);
442 	}
443 	return (retval);
444 }
445 
446 /*
447  * parse args and status hotspares
448  */
449 static int
status_hotspares(mdsetname_t ** spp,int argc,char * argv[],md_error_t * ep)450 status_hotspares(
451 	mdsetname_t	**spp,
452 	int		argc,
453 	char		*argv[],
454 	md_error_t	*ep
455 )
456 {
457 	mdhspnamelist_t	*hspnlp = NULL;
458 	int		cnt;
459 	mdhspnamelist_t	*p;
460 	int		rval = -1;
461 
462 	/* get hotspare pool name(s) */
463 	if (argc == 0) {
464 		/* check for ownership */
465 		assert(*spp != NULL);
466 		if (meta_check_ownership(*spp, ep) != 0)
467 			return (-1);
468 
469 		if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) {
470 			return (-1);
471 		} else if (cnt == 0) {
472 			return (mderror(ep, MDE_NO_HSPS, NULL));
473 		}
474 	} else if ((cnt = metahspnamelist(spp, &hspnlp, argc, argv, ep)) < 0) {
475 		return (-1);
476 	}
477 	assert(cnt > 0);
478 
479 	/* check for ownership */
480 	assert(*spp != NULL);
481 	if (meta_check_ownership(*spp, ep) != 0)
482 		return (-1);
483 
484 	/* status hotspare pools */
485 	for (p = hspnlp; (p != NULL); p = p->next) {
486 		mdhspname_t	*hspnp = p->hspnamep;
487 
488 		if (status_hsp(*spp, hspnp, ep) != 0)
489 			goto out;
490 	}
491 
492 	if (print_hsp_devid(*spp, hspnlp, stdout, ep) == 0) {
493 		rval = 0;
494 	}
495 
496 	/* cleanup, return success */
497 out:
498 	if (hspnlp != NULL)
499 		metafreehspnamelist(hspnlp);
500 	return (rval);
501 }
502 
503 /*
504  * parse args and doit
505  */
506 int
main(int argc,char ** argv)507 main(
508 	int		argc,
509 	char		**argv
510 )
511 {
512 	char		*sname = MD_LOCAL_NAME;
513 	mdsetname_t	*sp = NULL;
514 	enum metahs_op	which_op = NONE;
515 	mdcmdopts_t	options = (MDCMD_PRINT | MDCMD_DOIT);
516 	int		c;
517 	md_error_t	status = mdnullerror;
518 	md_error_t	*ep = &status;
519 	int		error;
520 	bool_t		called_thru_rpc = FALSE;
521 	char		*cp;
522 
523 	/*
524 	 * Get the locale set up before calling any other routines
525 	 * with messages to ouput.  Just in case we're not in a build
526 	 * environment, make sure that TEXT_DOMAIN gets set to
527 	 * something.
528 	 */
529 #if !defined(TEXT_DOMAIN)
530 #define	TEXT_DOMAIN "SYS_TEST"
531 #endif
532 	(void) setlocale(LC_ALL, "");
533 	(void) textdomain(TEXT_DOMAIN);
534 
535 
536 	if ((cp = strstr(argv[0], ".rpc_call")) == NULL) {
537 		if (sdssc_bind_library() == SDSSC_OKAY)
538 			if (sdssc_cmd_proxy(argc, argv, SDSSC_PROXY_PRIMARY,
539 						&error) == SDSSC_PROXY_DONE)
540 				exit(error);
541 	} else {
542 		*cp = '\0'; /* cut off ".rpc_call" */
543 		called_thru_rpc = TRUE;
544 	}
545 
546 	/* initialize */
547 	if (md_init(argc, argv, 0, 1, ep) != 0) {
548 		mde_perror(ep, "");
549 		md_exit(sp, 1);
550 	}
551 
552 	/* parse args */
553 	optind = 1;
554 	opterr = 1;
555 	while ((c = getopt(argc, argv, "hs:aderin?")) != -1) {
556 		switch (c) {
557 		case 'h':
558 			usage(sp, 0);
559 			break;
560 
561 		case 's':
562 			sname = optarg;
563 			break;
564 
565 		case 'a':
566 			if (which_op != NONE)
567 				usage(sp, 1);
568 			which_op = ADD_A_HS;
569 			break;
570 
571 		case 'd':
572 			if (which_op != NONE)
573 				usage(sp, 1);
574 			which_op = DELETE_A_HS;
575 			break;
576 
577 		case 'e':
578 			if (which_op != NONE)
579 				usage(sp, 1);
580 			which_op = ENABLE_A_HS;
581 			break;
582 
583 		case 'r':
584 			if (which_op != NONE)
585 				usage(sp, 1);
586 			which_op = REPLACE_A_HS;
587 			break;
588 
589 		case 'i':
590 			if (which_op != NONE)
591 				usage(sp, 1);
592 			which_op = STATUS_A_HSP;
593 			break;
594 
595 		case 'n':
596 			if (called_thru_rpc == TRUE) {
597 				options &= ~MDCMD_DOIT;
598 			} else {
599 				usage(sp, 1);
600 			}
601 			break;
602 
603 
604 		case '?':
605 			if (optopt == '?')
606 				usage(sp, 0);
607 			/*FALLTHROUGH*/
608 		default:
609 			usage(sp, 1);
610 			break;
611 		}
612 	}
613 
614 	/* get set context */
615 	if ((sp = metasetname(sname, ep)) == NULL) {
616 		mde_perror(ep, "");
617 		md_exit(sp, 1);
618 	}
619 
620 	/*
621 	 * Send the command to all nodes if the -s argument refers to a MN
622 	 * set or the next argument refers to MN set hotspare name ( argc
623 	 * greater than optind if there is a next argument)
624 	 */
625 	if ((called_thru_rpc == FALSE) &&
626 	    (meta_is_mn_set(sp, ep) || ((argc > optind) &&
627 	    meta_is_mn_name(&sp, argv[optind], ep)))) {
628 		int	i;
629 		int	newargc;
630 		int	result;
631 		char	**newargv;
632 
633 		/*
634 		 * If we are dealing with a MN set and we were not
635 		 * called thru an rpc call, we are just to send this
636 		 * command string to the master of the set and let it
637 		 * deal with it.
638 		 * First we send out a dryrun version of this command.
639 		 * If that returns success, we know it succeeded on all
640 		 * nodes and it is safe to do the real command now.
641 		 */
642 		newargv = calloc(argc+1, sizeof (char *));
643 		newargv[0] = "metahs";
644 		newargv[1] = "-n"; /* always do "-n" first */
645 		newargc = 2;
646 		for (i = 1; i < argc; i++, newargc++)
647 			newargv[newargc] = argv[i];
648 		result = meta_mn_send_command(sp, newargc, newargv,
649 		    MD_DISP_STDERR | MD_DRYRUN, NO_CONTEXT_STRING, ep);
650 
651 		/* If we found a problem don't do it for real */
652 		if (result != 0) {
653 			md_exit(sp, result);
654 		}
655 
656 		/*
657 		 * Do it for real now. Remove "-n" from the arguments and
658 		 * MD_DRYRUN from the flags. If this fails the master must panic
659 		 * as the mddbs may be inconsistent.
660 		 */
661 		newargv[1] = ""; /* this was "-n" before */
662 		result = meta_mn_send_command(sp, newargc, newargv,
663 		    MD_DISP_STDERR | MD_RETRY_BUSY | MD_PANIC_WHEN_INCONSISTENT,
664 		    NO_CONTEXT_STRING, ep);
665 		free(newargv);
666 
667 		/* No further action required */
668 		md_exit(sp, result);
669 	}
670 
671 	argc -= optind;
672 	argv += optind;
673 	if (which_op == NONE)
674 		usage(sp, 1);
675 
676 	/*
677 	 * if a hot spare pool was specified by name then
678 	 * get the canonical form of the name and set up
679 	 * sp if the name was specified in the form 'set/hsp'
680 	 * unless 'all' is specified or the request is made to
681 	 * enable a hs which means that argv[0] will be a component
682 	 */
683 	if (argc > 0 && !meta_is_all(argv[0]) && which_op != ENABLE_A_HS) {
684 		char *cname = NULL;
685 
686 		cname = meta_name_getname(&sp, argv[0], HSP_DEVICE, ep);
687 		if (cname == NULL) {
688 			mde_perror(ep, "");
689 			md_exit(sp, 1);
690 		}
691 		Free(cname);
692 	}
693 
694 	if (which_op == STATUS_A_HSP) {
695 		if (status_hotspares(&sp, argc, argv, ep) != 0) {
696 			mde_perror(ep, "");
697 			md_exit(sp, 1);
698 		}
699 		md_exit(sp, 0);
700 	}
701 
702 	if (meta_check_root(ep) != 0) {
703 		mde_perror(ep, "");
704 		md_exit(sp, 1);
705 	}
706 
707 
708 	/* dispatch */
709 	switch (which_op) {
710 
711 	case ADD_A_HS:
712 		if (add_hotspares(&sp, argc, argv, options, ep) != 0) {
713 			mde_perror(ep, "");
714 			md_exit(sp, 1);
715 		}
716 		break;
717 
718 	case DELETE_A_HS:
719 		if (delete_hotspares(&sp, argc, argv, options, ep) != 0) {
720 			mde_perror(ep, "");
721 			md_exit(sp, 1);
722 		}
723 		break;
724 
725 	case ENABLE_A_HS:
726 		if (enable_hotspares(&sp, argc, argv, options, ep) != 0) {
727 			mde_perror(ep, "");
728 			md_exit(sp, 1);
729 		}
730 		break;
731 
732 	case REPLACE_A_HS:
733 		if (replace_hotspares(&sp, argc, argv, options, ep) != 0) {
734 			mde_perror(ep, "");
735 			md_exit(sp, 1);
736 		}
737 		break;
738 
739 	default:
740 		assert(0);
741 		break;
742 	}
743 
744 	/* update md.cf */
745 out:
746 	if (meta_update_md_cf(sp, ep) != 0) {
747 		mde_perror(ep, "");
748 		md_exit(sp, 1);
749 	}
750 	md_exit(sp, 0);
751 	/*NOTREACHED*/
752 	return (0);
753 }
754