xref: /titanic_41/usr/src/cmd/lvm/util/metaroot.c (revision ba7866cd2cbdf574f47d4e38a1301b90744dd677)
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  * patch system files for root on metadevice
30  */
31 
32 #include <meta.h>
33 #include <stdlib.h>
34 #include <sdssc.h>
35 
36 #define	METAROOT_OK 0
37 #define	METAROOT_ERR -1
38 #define	METAROOT_NOTFOUND -2
39 
40 struct def_map {
41 	char		**dm_fname;	/* Location of file name */
42 	char		*dm_default;	/* Default name */
43 };
44 
45 /*
46  * options
47  */
48 static	char	*cname = NULL;	/* take default */
49 static	char	*sname = NULL;	/* take default */
50 static	char	*vname = NULL;	/* take default */
51 static	char	*dbname = NULL;	/* take default bootlist location */
52 static	int	doit = 1;
53 static	int	verbose = 0;
54 
55 /*
56  * Map of default system file names to the place where they are stored.
57  * This is used if the -R option is specified.  Note that the members of
58  * the map point to the cname, sname, vname and dbname global variables
59  * above.  These global variables are used in the call to
60  * meta_patch_rootdev() in main().
61  */
62 static struct def_map	default_names[] = {
63 	&cname, META_DBCONF,
64 	&sname, "/etc/system",
65 	&vname, "/etc/vfstab",
66 	&dbname, "/kernel/drv/md.conf"
67 };
68 
69 static int validate_stripe_root();
70 
71 /*
72  * print usage message, md_exit
73  */
74 static void
75 usage(
76 	mdsetname_t	*sp,
77 	int		eval
78 )
79 {
80 	(void) fprintf(stderr, gettext("\
81 usage:\t%s [-n] [-k system-name] [-m md.conf-name] [-v vfstab-name] \\\n\
82 \t\t[-c mddb.cf-name] device\n\
83 \t%s [-n] [-R root-path] device\n"),
84 	    myname, myname);
85 	md_exit(sp, eval);
86 }
87 
88 static void
89 free_mem()
90 {
91 	int			i;
92 	struct def_map		*map;
93 
94 	for (i = 0, map = default_names;
95 		i < sizeof (default_names) / sizeof (struct def_map);
96 		i++, map++) {
97 		if (*map->dm_fname != NULL) {
98 			free((void *) *map->dm_fname);
99 			*map->dm_fname = NULL;
100 		}
101 	}
102 }
103 
104 /*
105  * Check if mirror, mirnp, is a valid root filesystem, ie all
106  * submirrors must be single disk stripe, and that the slice, slicenp,
107  * if not NULL, is a component of one of the submirrors.
108  * The arg metaroot is TRUE if mirnp is the current root filesystem.
109  * Returns:
110  * METAROOT_OK		if mirror is valid and slicenp is a component
111  * METAROOT_NOTFOUND	if mirror valid but slicenp not a component
112  * METAROOT_ERR		if mirror not a valid root
113  */
114 static int
115 validate_mirror_root(
116 	mdsetname_t	*sp,
117 	mdname_t	*mirnp,
118 	mdname_t	*slicenp,
119 	int		metaroot,
120 	md_error_t	*ep
121 )
122 {
123 	int 		smi;
124 	md_mirror_t	*mirrorp;
125 	char		*miscname;
126 	int		found = 0;
127 	int		rval;
128 	int		err = 0;
129 
130 	if ((mirrorp = meta_get_mirror(sp, mirnp, ep)) == NULL) {
131 		mde_perror(ep, "");
132 		return (METAROOT_ERR);
133 	}
134 
135 	for (smi = 0; (smi < NMIRROR); ++smi) {
136 		/* Check all submirrors */
137 		md_submirror_t  *mdsp = &mirrorp->submirrors[smi];
138 		mdname_t	*submirnamep = mdsp->submirnamep;
139 
140 		/* skip unused submirrors */
141 		if (submirnamep == NULL) {
142 			assert(mdsp->state == SMS_UNUSED);
143 			continue;
144 		}
145 		if ((miscname = metagetmiscname(submirnamep, ep)) == NULL) {
146 			return (mdmderror(ep, MDE_UNKNOWN_TYPE,
147 					meta_getminor(submirnamep->dev),
148 					submirnamep->cname));
149 		}
150 		if (strcmp(miscname, MD_STRIPE) != 0) {
151 			md_eprintf(gettext("Submirror is not a stripe\n"));
152 			return (METAROOT_ERR);
153 		}
154 		rval = validate_stripe_root(sp, submirnamep, slicenp,
155 		    metaroot, ep);
156 		switch (rval) {
157 		case METAROOT_OK:
158 			found = 1;
159 			break;
160 		case METAROOT_ERR:
161 			err++;
162 			break;
163 		case METAROOT_NOTFOUND:
164 		default:
165 			break;
166 		}
167 	}
168 	if (err > 0)
169 		return (METAROOT_ERR);
170 	if (!found)
171 		return (METAROOT_NOTFOUND);
172 	return (METAROOT_OK);
173 }
174 
175 /*
176  * Check if stripe, strnp, is a valid root filesystem, ie must
177  * be single disk stripe, and the the slice, slicenp, if not NULL, must
178  * be a component of this stripe.
179  * The arg metaroot is TRUE if strnp is the current root filesystem.
180  * Returns:
181  * METAROOT_OK		if stripe is valid and slicenp is a component
182  * METAROOT_NOTFOUND	if stripe valid but slicenp not a component
183  * METAROOT_ERR		if stripe not a valid root
184  */
185 static int
186 validate_stripe_root(
187 	mdsetname_t	*sp,
188 	mdname_t	*strnp,
189 	mdname_t	*slicenp,
190 	int		metaroot,
191 	md_error_t	*ep
192 )
193 {
194 	md_stripe_t	*stripep;
195 	md_row_t	*rp;
196 	md_comp_t	*cp;
197 
198 	if ((stripep = meta_get_stripe(sp, strnp, ep)) == NULL) {
199 		mde_perror(ep, "");
200 		return (METAROOT_ERR);
201 	}
202 	if (stripep->rows.rows_len != 1) {
203 		md_eprintf(gettext(
204 		    "Concat %s has more than 1 slice\n"), strnp->cname);
205 		return (METAROOT_ERR);
206 	}
207 	rp = &stripep->rows.rows_val[0];
208 
209 	if (rp->comps.comps_len != 1) {
210 		md_eprintf(gettext(
211 		    "Stripe %s has more than 1 slice\n"), strnp->cname);
212 		return (METAROOT_ERR);
213 	}
214 	cp = &rp->comps.comps_val[0];
215 	if (!metaismeta(cp->compnamep)) {
216 		if (slicenp == NULL)
217 			return (METAROOT_OK);
218 		if (strcmp(slicenp->cname, cp->compnamep->cname) == 0)
219 			return (METAROOT_OK);
220 		if (!metaroot) {
221 			md_eprintf(gettext(
222 			    "Root %s is not a component of metadevice %s\n"),
223 			    slicenp->cname, strnp->cname);
224 		}
225 		return (METAROOT_NOTFOUND);
226 	}
227 	md_eprintf(gettext(
228 	    "Component %s is not a stripe\n"), cp->compnamep->cname);
229 	return (METAROOT_ERR);
230 }
231 
232 /*
233  * Check if the device devnp is valid. It must be a component of the
234  * metadevice that contains the root filesystem
235  */
236 
237 static int
238 validate_root_device(
239 	mdsetname_t	*sp,
240 	mdname_t	*devnp,
241 	md_error_t	*ep
242 )
243 {
244 	mdname_t	*rootnp;
245 	char		*curroot;
246 	char		*miscname;
247 	int		rval;
248 
249 	if ((curroot = meta_get_current_root(ep)) == NULL) {
250 		mde_perror(ep, "");
251 		return (METAROOT_ERR);
252 	}
253 	if ((rootnp = metaname(&sp, curroot, UNKNOWN, ep)) == NULL) {
254 		mde_perror(ep, "");
255 		return (METAROOT_ERR);
256 	}
257 
258 	if (metaismeta(rootnp)) {
259 		/* get type */
260 		if ((miscname = metagetmiscname(rootnp, ep)) == NULL) {
261 			mde_perror(ep, "");
262 			return (METAROOT_ERR);
263 		}
264 		if (strcmp(miscname, MD_MIRROR) == 0) {
265 			if ((rval = validate_mirror_root(sp, rootnp,
266 			    devnp, 1, ep)) == METAROOT_OK)
267 				return (METAROOT_OK);
268 			if (rval == METAROOT_NOTFOUND) {
269 				md_eprintf(gettext(
270 				    "Slice %s is not a component of root %s\n"),
271 				    devnp->cname, rootnp->cname);
272 			}
273 			return (METAROOT_ERR);
274 		} else if (strcmp(miscname, MD_STRIPE) == 0) {
275 			if ((rval = validate_stripe_root(sp, rootnp,
276 			    devnp, 1, ep)) == METAROOT_OK)
277 				return (METAROOT_OK);
278 			if (rval == METAROOT_NOTFOUND) {
279 				md_eprintf(gettext(
280 				    "Slice %s is not a component of root %s\n"),
281 				    devnp->cname, rootnp->cname);
282 			}
283 			return (METAROOT_ERR);
284 		} else {
285 			md_eprintf(gettext(
286 			    "Root metadevice, %s, is not a Slice or Mirror\n"),
287 			    rootnp->cname);
288 			return (METAROOT_ERR);
289 		}
290 	} else {
291 		md_eprintf(gettext(
292 		    "Current Root %s is not a metadevice\n"), rootnp->cname);
293 		return (METAROOT_ERR);
294 	}
295 }
296 
297 /*
298  * What we're going to do:
299  *
300  * 1) Check if the device is a metadevice or not.
301  *
302  * 2) If a metadevice, and it is valid, ie a stripe or a mirror containing
303  *    a single slice, add "forceload:{drv,misc}/<modname>" of
304  *    underlying drivers for the meta-root and the metadevice
305  *    database to system. Otherwise, remove forceloads from system if the
306  *    slice is a component of the current root metadevice.
307  *
308  * 3) Add "rootdev:/devices/..." to system.
309  *
310  * 4) Replace / mount in vfstab.
311  *
312  * 5) Repatch database locations, just to be safe.
313  */
314 int
315 main(
316 	int		argc,
317 	char		*argv[]
318 )
319 {
320 	int		i;
321 	mdsetname_t	*sp = NULL;
322 	mdname_t	*rootnp;
323 	int		c;
324 	int		ckmv_flag = 0;	/* non-zero if -c, -k, -m or -v */
325 	md_error_t	status = mdnullerror;
326 	md_error_t	*ep = &status;
327 	char		*miscname;
328 	char		*curroot;
329 	mdname_t	*currootnp;
330 	mdname_t	*currootdevnp;
331 	char		*root_path = NULL;
332 	struct def_map	*map;
333 	size_t		root_path_size;
334 	size_t		path_buf_size;
335 	int		error;
336 
337 	/*
338 	 * Get the locale set up before calling any other routines
339 	 * with messages to ouput.  Just in case we're not in a build
340 	 * environment, make sure that TEXT_DOMAIN gets set to
341 	 * something.
342 	 */
343 #if !defined(TEXT_DOMAIN)
344 #define	TEXT_DOMAIN "SYS_TEST"
345 #endif
346 	(void) setlocale(LC_ALL, "");
347 	(void) textdomain(TEXT_DOMAIN);
348 
349 	if ((sdssc_bind_library() == SDSSC_OKAY) &&
350 		(sdssc_cmd_proxy(argc, argv, SDSSC_PROXY_PRIMARY,
351 		    &error) == SDSSC_PROXY_DONE))
352 			exit(error);
353 
354 	/* initialize */
355 	if (md_init(argc, argv, 0, 1, ep) != 0 ||
356 			meta_check_root(ep) != 0) {
357 		mde_perror(ep, "");
358 		md_exit(sp, 1);
359 	}
360 
361 	/* parse options */
362 	optind = 1;
363 	opterr = 1;
364 	while ((c = getopt(argc, argv, "hnk:m:v:c:R:?")) != -1) {
365 		switch (c) {
366 		case 'h':
367 			usage(sp, 0);
368 			break;
369 		case 'm':
370 			dbname = optarg;
371 			ckmv_flag = 1;
372 			break;
373 		case 'n':
374 			doit = 0;
375 			verbose = 1;
376 			break;
377 		case 'k':
378 			sname = optarg;
379 			ckmv_flag = 1;
380 			break;
381 		case 'v':
382 			vname = optarg;
383 			ckmv_flag = 1;
384 			break;
385 		case 'c':
386 			cname = optarg;
387 			ckmv_flag = 1;
388 			break;
389 		case 'R':
390 			root_path = optarg;
391 			break;
392 		case '?':
393 			if (optopt == '?')
394 				usage(sp, 0);
395 			/*FALLTHROUGH*/
396 		default:
397 			usage(sp, 1);
398 			break;
399 		}
400 	}
401 	argc -= optind;
402 	argv += optind;
403 	if (argc != 1)
404 		usage(sp, 1);
405 
406 	/* Can't use -R with any of -c, -k, -m or -v */
407 	if ((ckmv_flag != 0) && (root_path != NULL)) {
408 		md_eprintf(
409 			gettext("-R invalid with any of -c, -k, -m or -v\n"));
410 		usage(sp, 1);
411 	}
412 
413 	/* get device name */
414 	if ((rootnp = metaname(&sp, argv[0], UNKNOWN, ep)) == NULL) {
415 		mde_perror(ep, "");
416 		md_exit(sp, 1);
417 	}
418 	if ((curroot = meta_get_current_root(ep)) == NULL) {
419 		mde_perror(ep, "");
420 		md_exit(sp, 1);
421 	}
422 	/*
423 	 * Get device name of current root metadevice.  If root is net
424 	 * mounted as happens if this command is part of the install
425 	 * process, currootnp will be set to NULL.
426 	 */
427 	currootnp = metaname(&sp, curroot, UNKNOWN, ep);
428 	/*
429 	 * If the argument is the name of the current root filesystem, then
430 	 * the command is allowed, otherwise check that the argument is
431 	 * valid.
432 	 */
433 	if ((currootnp == NULL) ||
434 		(strcmp(currootnp->cname, rootnp->cname) != 0)) {
435 		if (metaismeta(rootnp)) {
436 			/*
437 			 * Validate that the metadevice is based on a
438 			 * single slice. If none of the -k, -m, -v, -c or
439 			 * -R options are specified, then the default
440 			 * system files are being modified and hence the
441 			 * current root slice must be a component of the
442 			 * metadevice. If any of the previously mentioned
443 			 * options are used don't check that the current
444 			 * root is a component.
445 			 */
446 			if ((ckmv_flag == 0) && (root_path == NULL)) {
447 				/* Get device name of current root slice */
448 				if ((currootdevnp =
449 				    meta_get_current_root_dev(sp, ep))
450 				    == NULL) {
451 					mde_perror(ep, "");
452 					md_exit(sp, 1);
453 				}
454 			} else currootdevnp = NULL;
455 
456 			if ((miscname = metagetmiscname(rootnp, ep)) == NULL) {
457 				mde_perror(ep, "");
458 				md_exit(sp, 1);
459 			}
460 			/* Check that metadevice is a mirror or a stripe */
461 			if (strcmp(miscname, MD_MIRROR) == 0) {
462 				if (validate_mirror_root(sp, rootnp,
463 				    currootdevnp, 0, ep) != METAROOT_OK) {
464 					md_exit(sp, 1);
465 				}
466 			} else if (strcmp(miscname, MD_STRIPE) == 0) {
467 				if (validate_stripe_root(sp, rootnp,
468 				    currootdevnp, 0, ep) != METAROOT_OK) {
469 					md_exit(sp, 1);
470 				}
471 			} else {
472 				md_eprintf(gettext(
473 				    "%s is not a mirror or stripe\n"),
474 				    rootnp->cname);
475 				md_exit(sp, 1);
476 			}
477 		} else {
478 			/*
479 			 * Check that the root device is a component of the
480 			 * current root filesystem only if the default system
481 			 * files are being modified
482 			 */
483 			if ((ckmv_flag == 0) && (root_path == NULL)) {
484 				if (validate_root_device(sp, rootnp, ep) != 0) {
485 					md_exit(sp, 1);
486 				}
487 			}
488 		}
489 	}
490 
491 	if (meta_lock(sp, TRUE, ep)) {
492 		mde_perror(ep, "");
493 		md_exit(sp, 1);
494 	}
495 
496 	/*
497 	 * If -R is specified, use the default system file names relative
498 	 * to the new root location.
499 	 */
500 	if (root_path != NULL) {
501 		root_path_size = strlen(root_path);
502 		for (i = 0, map = default_names;
503 			i < sizeof (default_names) / sizeof (struct def_map);
504 			i++, map++) {
505 			/* Add 1 for null terminator */
506 			path_buf_size = root_path_size +
507 				strlen(map->dm_default) + 1;
508 			*map->dm_fname = malloc(path_buf_size);
509 			if (*map->dm_fname == NULL) {
510 				md_eprintf(gettext("Cannot allocate memory \
511 for system file path relocation\n"));
512 				md_exit(sp, 1);
513 			}
514 			(void) snprintf(*map->dm_fname, path_buf_size,
515 					"%s%s", root_path, map->dm_default);
516 		}
517 	}
518 
519 	/* patch system and vfstab for root and mddb locations */
520 	if (meta_patch_rootdev(rootnp, sname, vname, cname, dbname, doit,
521 	    verbose, ep) != 0) {
522 		if (root_path != NULL) {
523 			free_mem();
524 		}
525 		mde_perror(ep, "");
526 		md_exit(sp, 1);
527 	}
528 	if (root_path != NULL) {
529 		free_mem();
530 	}
531 
532 	/* return success */
533 	md_exit(sp, 0);
534 	/*NOTREACHED*/
535 	return (0);
536 }
537