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
40struct def_map {
41	char		**dm_fname;	/* Location of file name */
42	char		*dm_default;	/* Default name */
43};
44
45/*
46 * options
47 */
48static	char	*cname = NULL;	/* take default */
49static	char	*sname = NULL;	/* take default */
50static	char	*vname = NULL;	/* take default */
51static	char	*dbname = NULL;	/* take default bootlist location */
52static	int	doit = 1;
53static	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 */
62static 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
69static int validate_stripe_root();
70
71/*
72 * print usage message, md_exit
73 */
74static void
75usage(
76	mdsetname_t	*sp,
77	int		eval
78)
79{
80	(void) fprintf(stderr, gettext("\
81usage:\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
88static void
89free_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 */
114static int
115validate_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 */
185static int
186validate_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
237static int
238validate_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 */
314int
315main(
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 \
511for 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