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
usage(mdsetname_t * sp,int eval)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
free_mem()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
validate_mirror_root(mdsetname_t * sp,mdname_t * mirnp,mdname_t * slicenp,int metaroot,md_error_t * ep)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
validate_stripe_root(mdsetname_t * sp,mdname_t * strnp,mdname_t * slicenp,int metaroot,md_error_t * ep)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
validate_root_device(mdsetname_t * sp,mdname_t * devnp,md_error_t * ep)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
main(int argc,char * argv[])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