Print this page
10117 libbe needs smatch fixes
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/libbe/common/be_utils.c
+++ new/usr/src/lib/libbe/common/be_utils.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
↓ open down ↓ |
17 lines elided |
↑ open up ↑ |
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25 25 * Copyright 2016 Toomas Soome <tsoome@me.com>
26 26 * Copyright (c) 2015 by Delphix. All rights reserved.
27 27 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
28 + * Copyright (c) 2018, Joyent, Inc.
28 29 */
29 30
30 31
31 32 /*
32 33 * System includes
33 34 */
34 35 #include <assert.h>
35 36 #include <errno.h>
36 37 #include <libgen.h>
37 38 #include <libintl.h>
38 39 #include <libnvpair.h>
39 40 #include <libzfs.h>
40 41 #include <libgen.h>
41 42 #include <stdio.h>
42 43 #include <stdlib.h>
43 44 #include <string.h>
44 45 #include <sys/stat.h>
45 46 #include <sys/types.h>
46 47 #include <sys/vfstab.h>
47 48 #include <sys/param.h>
48 49 #include <sys/systeminfo.h>
49 50 #include <ctype.h>
50 51 #include <time.h>
51 52 #include <unistd.h>
52 53 #include <fcntl.h>
53 54 #include <deflt.h>
54 55 #include <wait.h>
55 56 #include <libdevinfo.h>
56 57 #include <libgen.h>
57 58
58 59 #include <libbe.h>
59 60 #include <libbe_priv.h>
60 61 #include <boot_utils.h>
61 62 #include <ficl.h>
62 63 #include <ficlplatform/emu.h>
63 64
64 65 /* Private function prototypes */
65 66 static int update_dataset(char *, int, char *, char *, char *);
66 67 static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *);
67 68 static int be_open_menu(char *, char *, FILE **, char *, boolean_t);
68 69 static int be_create_menu(char *, char *, FILE **, char *);
69 70 static char *be_get_auto_name(char *, char *, boolean_t);
70 71
71 72 /*
72 73 * Global error printing
73 74 */
74 75 boolean_t do_print = B_FALSE;
75 76
76 77 /*
77 78 * Private datatypes
78 79 */
79 80 typedef struct zone_be_name_cb_data {
80 81 char *base_be_name;
81 82 int num;
82 83 } zone_be_name_cb_data_t;
83 84
84 85 /* ******************************************************************** */
85 86 /* Public Functions */
86 87 /* ******************************************************************** */
87 88
88 89 /*
89 90 * Callback for ficl to suppress all output from ficl, as we do not
90 91 * want to confuse user with messages from ficl, and we are only
91 92 * checking results from function calls.
92 93 */
93 94 /*ARGSUSED*/
94 95 static void
95 96 ficlSuppressTextOutput(ficlCallback *cb, char *text)
96 97 {
97 98 /* This function is intentionally doing nothing. */
98 99 }
99 100
100 101 /*
101 102 * Function: be_get_boot_args
102 103 * Description: Returns the fast boot argument string for enumerated BE.
103 104 * Parameters:
104 105 * fbarg - pointer to argument string.
105 106 * entry - index of BE.
106 107 * Returns:
107 108 * fast boot argument string.
108 109 * Scope:
109 110 * Public
110 111 */
111 112 int
112 113 be_get_boot_args(char **fbarg, int entry)
113 114 {
114 115 be_node_list_t *node, *be_nodes = NULL;
115 116 be_transaction_data_t bt = {0};
116 117 char *mountpoint = NULL;
117 118 boolean_t be_mounted = B_FALSE;
118 119 int ret = BE_SUCCESS;
119 120 int index;
120 121 ficlVm *vm;
121 122
122 123 *fbarg = NULL;
123 124 if (!be_zfs_init())
124 125 return (BE_ERR_INIT);
125 126
126 127 /*
127 128 * need pool name, menu.lst has entries from our pool only
128 129 */
129 130 ret = be_find_current_be(&bt);
130 131 if (ret != BE_SUCCESS) {
131 132 be_zfs_fini();
132 133 return (ret);
133 134 }
134 135
135 136 /*
136 137 * be_get_boot_args() is for loader, fail with grub will trigger
137 138 * normal boot.
138 139 */
139 140 if (be_has_grub()) {
140 141 ret = BE_ERR_INIT;
141 142 goto done;
142 143 }
143 144
144 145 ret = _be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
145 146 if (ret != BE_SUCCESS)
146 147 goto done;
147 148
148 149 /*
149 150 * iterate through be_nodes,
150 151 * if entry == -1, stop if be_active_on_boot,
151 152 * else stop if index == entry.
152 153 */
153 154 index = 0;
154 155 for (node = be_nodes; node != NULL; node = node->be_next_node) {
155 156 if (strcmp(node->be_rpool, bt.obe_zpool) != 0)
156 157 continue;
157 158 if (entry == BE_ENTRY_DEFAULT &&
158 159 node->be_active_on_boot == B_TRUE)
159 160 break;
160 161 if (index == entry)
161 162 break;
162 163 index++;
163 164 }
164 165 if (node == NULL) {
165 166 be_free_list(be_nodes);
166 167 ret = BE_ERR_NOENT;
167 168 goto done;
168 169 }
169 170
170 171 /* try to mount inactive be */
171 172 if (node->be_active == B_FALSE) {
172 173 ret = _be_mount(node->be_node_name, &mountpoint,
173 174 BE_MOUNT_FLAG_NO_ZONES);
174 175 if (ret != BE_SUCCESS && ret != BE_ERR_MOUNTED) {
175 176 be_free_list(be_nodes);
176 177 goto done;
177 178 } else
178 179 be_mounted = B_TRUE;
179 180 }
180 181
181 182 vm = bf_init("", ficlSuppressTextOutput);
182 183 if (vm != NULL) {
183 184 /*
184 185 * zfs MAXNAMELEN is 256, so we need to pick buf large enough
185 186 * to contain such names.
186 187 */
187 188 char buf[MAXNAMELEN * 2];
188 189 char *kernel_options = NULL;
189 190 char *kernel = NULL;
190 191 char *tmp;
191 192 zpool_handle_t *zph;
192 193
193 194 /*
194 195 * just try to interpret following words. on error
195 196 * we will be missing kernelname, and will get out.
196 197 */
197 198 (void) snprintf(buf, sizeof (buf), "set currdev=zfs:%s:",
198 199 node->be_root_ds);
199 200 ret = ficlVmEvaluate(vm, buf);
200 201 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
201 202 be_print_err(gettext("be_get_boot_args: error "
202 203 "interpreting boot config: %d\n"), ret);
203 204 bf_fini();
204 205 ret = BE_ERR_NO_MENU;
205 206 goto cleanup;
206 207 }
207 208 (void) snprintf(buf, sizeof (buf),
208 209 "include /boot/forth/loader.4th");
209 210 ret = ficlVmEvaluate(vm, buf);
210 211 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
211 212 be_print_err(gettext("be_get_boot_args: error "
212 213 "interpreting boot config: %d\n"), ret);
213 214 bf_fini();
214 215 ret = BE_ERR_NO_MENU;
215 216 goto cleanup;
216 217 }
217 218 (void) snprintf(buf, sizeof (buf), "start");
218 219 ret = ficlVmEvaluate(vm, buf);
219 220 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
220 221 be_print_err(gettext("be_get_boot_args: error "
221 222 "interpreting boot config: %d\n"), ret);
222 223 bf_fini();
223 224 ret = BE_ERR_NO_MENU;
224 225 goto cleanup;
225 226 }
226 227 (void) snprintf(buf, sizeof (buf), "boot");
227 228 ret = ficlVmEvaluate(vm, buf);
228 229 bf_fini();
229 230 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
230 231 be_print_err(gettext("be_get_boot_args: error "
231 232 "interpreting boot config: %d\n"), ret);
232 233 ret = BE_ERR_NO_MENU;
233 234 goto cleanup;
234 235 }
235 236
236 237 kernel_options = getenv("boot-args");
237 238 kernel = getenv("kernelname");
238 239
239 240 if (kernel == NULL) {
240 241 be_print_err(gettext("be_get_boot_args: no kernel\n"));
241 242 ret = BE_ERR_NOENT;
242 243 goto cleanup;
243 244 }
244 245
245 246 if ((zph = zpool_open(g_zfs, node->be_rpool)) == NULL) {
246 247 be_print_err(gettext("be_get_boot_args: failed to "
247 248 "open root pool (%s): %s\n"), node->be_rpool,
248 249 libzfs_error_description(g_zfs));
249 250 ret = zfs_err_to_be_err(g_zfs);
250 251 goto cleanup;
251 252 }
252 253 ret = zpool_get_physpath(zph, buf, sizeof (buf));
253 254 zpool_close(zph);
254 255 if (ret != 0) {
255 256 be_print_err(gettext("be_get_boot_args: failed to "
256 257 "get physpath\n"));
257 258 goto cleanup;
258 259 }
259 260
260 261 /* zpool_get_physpath() can return space separated list */
261 262 tmp = buf;
262 263 tmp = strsep(&tmp, " ");
↓ open down ↓ |
225 lines elided |
↑ open up ↑ |
263 264
264 265 if (kernel_options == NULL || *kernel_options == '\0')
265 266 (void) asprintf(fbarg, "/ %s "
266 267 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
267 268 node->be_root_ds, tmp);
268 269 else
269 270 (void) asprintf(fbarg, "/ %s %s "
270 271 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
271 272 kernel_options, node->be_root_ds, tmp);
272 273
273 - if (fbarg == NULL)
274 + if (*fbarg == NULL)
274 275 ret = BE_ERR_NOMEM;
275 276 else
276 277 ret = 0;
277 278 } else
278 279 ret = BE_ERR_NOMEM;
279 280 cleanup:
280 281 if (be_mounted == B_TRUE)
281 282 (void) _be_unmount(node->be_node_name, BE_UNMOUNT_FLAG_FORCE);
282 283 be_free_list(be_nodes);
283 284 done:
284 285 free(mountpoint);
285 286 free(bt.obe_name);
286 287 free(bt.obe_root_ds);
287 288 free(bt.obe_zpool);
288 289 free(bt.obe_snap_name);
289 290 free(bt.obe_altroot);
290 291 be_zfs_fini();
291 292 return (ret);
292 293 }
293 294
294 295 /*
295 296 * Function: be_max_avail
296 297 * Description: Returns the available size for the zfs dataset passed in.
297 298 * Parameters:
298 299 * dataset - The dataset we want to get the available space for.
299 300 * ret - The available size will be returned in this.
300 301 * Returns:
301 302 * The error returned by the zfs get property function.
302 303 * Scope:
303 304 * Public
304 305 */
305 306 int
306 307 be_max_avail(char *dataset, uint64_t *ret)
307 308 {
308 309 zfs_handle_t *zhp;
309 310 int err = 0;
310 311
311 312 /* Initialize libzfs handle */
312 313 if (!be_zfs_init())
313 314 return (BE_ERR_INIT);
314 315
315 316 zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
316 317 if (zhp == NULL) {
317 318 /*
318 319 * The zfs_open failed return an error
319 320 */
320 321 err = zfs_err_to_be_err(g_zfs);
321 322 } else {
322 323 err = be_maxsize_avail(zhp, ret);
323 324 }
324 325 ZFS_CLOSE(zhp);
325 326 be_zfs_fini();
326 327 return (err);
327 328 }
328 329
329 330 /*
330 331 * Function: libbe_print_errors
331 332 * Description: Turns on/off error output for the library.
332 333 * Parameter:
333 334 * set_do_print - Boolean that turns library error
334 335 * printing on or off.
335 336 * Returns:
336 337 * None
337 338 * Scope:
338 339 * Public;
339 340 */
340 341 void
341 342 libbe_print_errors(boolean_t set_do_print)
342 343 {
343 344 do_print = set_do_print;
344 345 }
345 346
346 347 /* ******************************************************************** */
347 348 /* Semi-Private Functions */
348 349 /* ******************************************************************** */
349 350
350 351 /*
351 352 * Function: be_zfs_init
352 353 * Description: Initializes the libary global libzfs handle.
353 354 * Parameters:
354 355 * None
355 356 * Returns:
356 357 * B_TRUE - Success
357 358 * B_FALSE - Failure
358 359 * Scope:
359 360 * Semi-private (library wide use only)
360 361 */
361 362 boolean_t
362 363 be_zfs_init(void)
363 364 {
364 365 be_zfs_fini();
365 366
366 367 if ((g_zfs = libzfs_init()) == NULL) {
367 368 be_print_err(gettext("be_zfs_init: failed to initialize ZFS "
368 369 "library\n"));
369 370 return (B_FALSE);
370 371 }
371 372
372 373 return (B_TRUE);
373 374 }
374 375
375 376 /*
376 377 * Function: be_zfs_fini
377 378 * Description: Closes the library global libzfs handle if it currently open.
378 379 * Parameter:
379 380 * None
380 381 * Returns:
381 382 * None
382 383 * Scope:
383 384 * Semi-private (library wide use only)
384 385 */
385 386 void
386 387 be_zfs_fini(void)
387 388 {
388 389 if (g_zfs)
389 390 libzfs_fini(g_zfs);
390 391
391 392 g_zfs = NULL;
392 393 }
393 394
394 395 /*
395 396 * Function: be_get_defaults
396 397 * Description: Open defaults and gets be default paramets
397 398 * Parameters:
398 399 * defaults - be defaults struct
399 400 * Returns:
400 401 * None
401 402 * Scope:
402 403 * Semi-private (library wide use only)
403 404 */
404 405 void
405 406 be_get_defaults(struct be_defaults *defaults)
406 407 {
407 408 void *defp;
408 409
409 410 defaults->be_deflt_grub = B_FALSE;
410 411 defaults->be_deflt_rpool_container = B_FALSE;
411 412 defaults->be_deflt_bename_starts_with[0] = '\0';
412 413
413 414 if ((defp = defopen_r(BE_DEFAULTS)) != NULL) {
414 415 const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp);
415 416 if (res != NULL && res[0] != '\0') {
416 417 (void) strlcpy(defaults->be_deflt_bename_starts_with,
417 418 res, ZFS_MAX_DATASET_NAME_LEN);
418 419 defaults->be_deflt_rpool_container = B_TRUE;
419 420 }
420 421 if (be_is_isa("i386")) {
421 422 res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
422 423 if (res != NULL && res[0] != '\0') {
423 424 if (strcasecmp(res, "true") == 0)
424 425 defaults->be_deflt_grub = B_TRUE;
425 426 }
426 427 }
427 428 defclose_r(defp);
428 429 }
429 430 }
430 431
431 432 /*
432 433 * Function: be_make_root_ds
433 434 * Description: Generate string for BE's root dataset given the pool
434 435 * it lives in and the BE name.
435 436 * Parameters:
436 437 * zpool - pointer zpool name.
437 438 * be_name - pointer to BE name.
438 439 * be_root_ds - pointer to buffer to return BE root dataset in.
439 440 * be_root_ds_size - size of be_root_ds
440 441 * Returns:
441 442 * None
442 443 * Scope:
443 444 * Semi-private (library wide use only)
444 445 */
445 446 void
446 447 be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
447 448 int be_root_ds_size)
448 449 {
449 450 struct be_defaults be_defaults;
450 451 be_get_defaults(&be_defaults);
451 452 char *root_ds = NULL;
452 453
453 454 if (getzoneid() == GLOBAL_ZONEID) {
454 455 if (be_defaults.be_deflt_rpool_container) {
455 456 (void) snprintf(be_root_ds, be_root_ds_size,
456 457 "%s/%s", zpool, be_name);
457 458 } else {
458 459 (void) snprintf(be_root_ds, be_root_ds_size,
459 460 "%s/%s/%s", zpool, BE_CONTAINER_DS_NAME, be_name);
460 461 }
461 462 } else {
462 463 /*
463 464 * In non-global zone we can use path from mounted root dataset
464 465 * to generate BE's root dataset string.
465 466 */
466 467 if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
467 468 (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s",
468 469 dirname(root_ds), be_name);
469 470 } else {
470 471 be_print_err(gettext("be_make_root_ds: zone root "
471 472 "dataset is not mounted\n"));
472 473 return;
473 474 }
474 475 }
475 476 }
476 477
477 478 /*
478 479 * Function: be_make_container_ds
479 480 * Description: Generate string for the BE container dataset given a pool name.
480 481 * Parameters:
481 482 * zpool - pointer zpool name.
482 483 * container_ds - pointer to buffer to return BE container
483 484 * dataset in.
484 485 * container_ds_size - size of container_ds
485 486 * Returns:
486 487 * None
487 488 * Scope:
488 489 * Semi-private (library wide use only)
489 490 */
490 491 void
491 492 be_make_container_ds(const char *zpool, char *container_ds,
492 493 int container_ds_size)
493 494 {
494 495 struct be_defaults be_defaults;
495 496 be_get_defaults(&be_defaults);
496 497 char *root_ds = NULL;
497 498
498 499 if (getzoneid() == GLOBAL_ZONEID) {
499 500 if (be_defaults.be_deflt_rpool_container) {
500 501 (void) snprintf(container_ds, container_ds_size,
501 502 "%s", zpool);
502 503 } else {
503 504 (void) snprintf(container_ds, container_ds_size,
504 505 "%s/%s", zpool, BE_CONTAINER_DS_NAME);
505 506 }
506 507 } else {
507 508 if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
508 509 (void) strlcpy(container_ds, dirname(root_ds),
509 510 container_ds_size);
510 511 } else {
511 512 be_print_err(gettext("be_make_container_ds: zone root "
512 513 "dataset is not mounted\n"));
513 514 return;
514 515 }
515 516 }
516 517 }
517 518
518 519 /*
519 520 * Function: be_make_name_from_ds
520 521 * Description: This function takes a dataset name and strips off the
521 522 * BE container dataset portion from the beginning. The
522 523 * returned name is allocated in heap storage, so the caller
523 524 * is responsible for freeing it.
524 525 * Parameters:
525 526 * dataset - dataset to get name from.
526 527 * rc_loc - dataset underwhich the root container dataset lives.
527 528 * Returns:
528 529 * name of dataset relative to BE container dataset.
529 530 * NULL if dataset is not under a BE root dataset.
530 531 * Scope:
531 532 * Semi-primate (library wide use only)
532 533 */
533 534 char *
534 535 be_make_name_from_ds(const char *dataset, char *rc_loc)
535 536 {
536 537 char ds[ZFS_MAX_DATASET_NAME_LEN];
537 538 char *tok = NULL;
538 539 char *name = NULL;
539 540 struct be_defaults be_defaults;
540 541 int rlen = strlen(rc_loc);
541 542
542 543 be_get_defaults(&be_defaults);
543 544
544 545 /*
545 546 * First token is the location of where the root container dataset
546 547 * lives; it must match rc_loc.
547 548 */
548 549 if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/')
549 550 (void) strlcpy(ds, dataset + rlen + 1, sizeof (ds));
550 551 else
551 552 return (NULL);
552 553
553 554 if (be_defaults.be_deflt_rpool_container) {
554 555 if ((name = strdup(ds)) == NULL) {
555 556 be_print_err(gettext("be_make_name_from_ds: "
556 557 "memory allocation failed\n"));
557 558 return (NULL);
558 559 }
559 560 } else {
560 561 /* Second token must be BE container dataset name */
561 562 if ((tok = strtok(ds, "/")) == NULL ||
562 563 strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
563 564 return (NULL);
564 565
565 566 /* Return the remaining token if one exists */
566 567 if ((tok = strtok(NULL, "")) == NULL)
567 568 return (NULL);
568 569
569 570 if ((name = strdup(tok)) == NULL) {
570 571 be_print_err(gettext("be_make_name_from_ds: "
571 572 "memory allocation failed\n"));
572 573 return (NULL);
573 574 }
574 575 }
575 576
576 577 return (name);
577 578 }
578 579
579 580 /*
580 581 * Function: be_maxsize_avail
581 582 * Description: Returns the available size for the zfs handle passed in.
582 583 * Parameters:
583 584 * zhp - A pointer to the open zfs handle.
584 585 * ret - The available size will be returned in this.
585 586 * Returns:
586 587 * The error returned by the zfs get property function.
587 588 * Scope:
588 589 * Semi-private (library wide use only)
589 590 */
590 591 int
591 592 be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
592 593 {
593 594 return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
594 595 }
595 596
596 597 /*
597 598 * Function: be_append_menu
598 599 * Description: Appends an entry for a BE into the menu.lst.
599 600 * Parameters:
600 601 * be_name - pointer to name of BE to add boot menu entry for.
601 602 * be_root_pool - pointer to name of pool BE lives in.
602 603 * boot_pool - Used if the pool containing the grub menu is
603 604 * different than the one contaiing the BE. This
604 605 * will normally be NULL.
605 606 * be_orig_root_ds - The root dataset for the BE. This is
606 607 * used to check to see if an entry already exists
607 608 * for this BE.
608 609 * description - pointer to description of BE to be added in
609 610 * the title line for this BEs entry.
610 611 * Returns:
611 612 * BE_SUCCESS - Success
612 613 * be_errno_t - Failure
613 614 * Scope:
614 615 * Semi-private (library wide use only)
615 616 */
616 617 int
617 618 be_append_menu(char *be_name, char *be_root_pool, char *boot_pool,
618 619 char *be_orig_root_ds, char *description)
619 620 {
620 621 zfs_handle_t *zhp = NULL;
621 622 char menu_file[MAXPATHLEN];
622 623 char be_root_ds[MAXPATHLEN];
623 624 char line[BUFSIZ];
624 625 char temp_line[BUFSIZ];
625 626 char title[MAXPATHLEN];
626 627 char *entries[BUFSIZ];
627 628 char *tmp_entries[BUFSIZ];
628 629 char *pool_mntpnt = NULL;
629 630 char *ptmp_mntpnt = NULL;
630 631 char *orig_mntpnt = NULL;
631 632 boolean_t found_be = B_FALSE;
632 633 boolean_t found_orig_be = B_FALSE;
633 634 boolean_t found_title = B_FALSE;
634 635 boolean_t pool_mounted = B_FALSE;
635 636 boolean_t collect_lines = B_FALSE;
636 637 FILE *menu_fp = NULL;
637 638 int err = 0, ret = BE_SUCCESS;
638 639 int i, num_tmp_lines = 0, num_lines = 0;
639 640
640 641 if (be_name == NULL || be_root_pool == NULL)
641 642 return (BE_ERR_INVAL);
642 643
643 644 if (boot_pool == NULL)
644 645 boot_pool = be_root_pool;
645 646
646 647 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
647 648 be_print_err(gettext("be_append_menu: failed to open "
648 649 "pool dataset for %s: %s\n"), be_root_pool,
649 650 libzfs_error_description(g_zfs));
650 651 return (zfs_err_to_be_err(g_zfs));
651 652 }
652 653
653 654 /*
654 655 * Check to see if the pool's dataset is mounted. If it isn't we'll
655 656 * attempt to mount it.
656 657 */
657 658 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
658 659 &pool_mounted)) != BE_SUCCESS) {
659 660 be_print_err(gettext("be_append_menu: pool dataset "
660 661 "(%s) could not be mounted\n"), be_root_pool);
661 662 ZFS_CLOSE(zhp);
662 663 return (ret);
663 664 }
664 665
665 666 /*
666 667 * Get the mountpoint for the root pool dataset.
667 668 */
668 669 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
669 670 be_print_err(gettext("be_append_menu: pool "
670 671 "dataset (%s) is not mounted. Can't set "
671 672 "the default BE in the grub menu.\n"), be_root_pool);
672 673 ret = BE_ERR_NO_MENU;
673 674 goto cleanup;
674 675 }
675 676
676 677 /*
677 678 * Check to see if this system supports grub
678 679 */
679 680 if (be_has_grub()) {
680 681 (void) snprintf(menu_file, sizeof (menu_file),
681 682 "%s%s", pool_mntpnt, BE_GRUB_MENU);
682 683 } else {
683 684 (void) snprintf(menu_file, sizeof (menu_file),
684 685 "%s%s", pool_mntpnt, BE_SPARC_MENU);
685 686 }
686 687
687 688 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
688 689
689 690 /*
690 691 * Iterate through menu first to make sure the BE doesn't already
691 692 * have an entry in the menu.
692 693 *
693 694 * Additionally while iterating through the menu, if we have an
694 695 * original root dataset for a BE we're cloning from, we need to keep
695 696 * track of that BE's menu entry. We will then use the lines from
696 697 * that entry to create the entry for the new BE.
697 698 */
698 699 if ((ret = be_open_menu(be_root_pool, menu_file,
699 700 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
700 701 goto cleanup;
701 702 } else if (menu_fp == NULL) {
702 703 ret = BE_ERR_NO_MENU;
703 704 goto cleanup;
704 705 }
705 706
706 707 free(pool_mntpnt);
707 708 pool_mntpnt = NULL;
708 709
709 710 while (fgets(line, BUFSIZ, menu_fp)) {
710 711 char *tok = NULL;
711 712
712 713 (void) strlcpy(temp_line, line, BUFSIZ);
713 714 tok = strtok(line, BE_WHITE_SPACE);
714 715
715 716 if (tok == NULL || tok[0] == '#') {
716 717 continue;
717 718 } else if (strcmp(tok, "title") == 0) {
718 719 collect_lines = B_FALSE;
719 720 if ((tok = strtok(NULL, "\n")) == NULL)
720 721 (void) strlcpy(title, "", sizeof (title));
721 722 else
722 723 (void) strlcpy(title, tok, sizeof (title));
723 724 found_title = B_TRUE;
724 725
725 726 if (num_tmp_lines != 0) {
726 727 for (i = 0; i < num_tmp_lines; i++) {
727 728 free(tmp_entries[i]);
728 729 tmp_entries[i] = NULL;
729 730 }
730 731 num_tmp_lines = 0;
731 732 }
732 733 } else if (strcmp(tok, "bootfs") == 0) {
733 734 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
734 735 found_title = B_FALSE;
735 736 if (bootfs == NULL)
736 737 continue;
737 738
738 739 if (strcmp(bootfs, be_root_ds) == 0) {
739 740 found_be = B_TRUE;
740 741 break;
741 742 }
742 743
743 744 if (be_orig_root_ds != NULL &&
744 745 strcmp(bootfs, be_orig_root_ds) == 0 &&
745 746 !found_orig_be) {
746 747 char str[BUFSIZ];
747 748 found_orig_be = B_TRUE;
748 749 num_lines = 0;
749 750 /*
750 751 * Store the new title line
751 752 */
752 753 (void) snprintf(str, BUFSIZ, "title %s\n",
753 754 description ? description : be_name);
754 755 entries[num_lines] = strdup(str);
755 756 num_lines++;
756 757 /*
757 758 * If there are any lines between the title
758 759 * and the bootfs line store these. Also
759 760 * free the temporary lines.
760 761 */
761 762 for (i = 0; i < num_tmp_lines; i++) {
762 763 entries[num_lines] = tmp_entries[i];
763 764 tmp_entries[i] = NULL;
764 765 num_lines++;
765 766 }
766 767 num_tmp_lines = 0;
767 768 /*
768 769 * Store the new bootfs line.
769 770 */
770 771 (void) snprintf(str, BUFSIZ, "bootfs %s\n",
771 772 be_root_ds);
772 773 entries[num_lines] = strdup(str);
773 774 num_lines++;
774 775 collect_lines = B_TRUE;
775 776 }
776 777 } else if (found_orig_be && collect_lines) {
777 778 /*
778 779 * get the rest of the lines for the original BE and
779 780 * store them.
780 781 */
781 782 if (strstr(line, BE_GRUB_COMMENT) != NULL ||
782 783 strstr(line, "BOOTADM") != NULL)
783 784 continue;
784 785 if (strcmp(tok, "splashimage") == 0) {
785 786 entries[num_lines] =
786 787 strdup("splashimage "
787 788 "/boot/splashimage.xpm\n");
788 789 } else {
789 790 entries[num_lines] = strdup(temp_line);
790 791 }
791 792 num_lines++;
792 793 } else if (found_title && !found_orig_be) {
793 794 tmp_entries[num_tmp_lines] = strdup(temp_line);
794 795 num_tmp_lines++;
795 796 }
796 797 }
797 798
798 799 (void) fclose(menu_fp);
799 800
800 801 if (found_be) {
801 802 /*
802 803 * If an entry for this BE was already in the menu, then if
803 804 * that entry's title matches what we would have put in
804 805 * return success. Otherwise return failure.
805 806 */
806 807 char *new_title = description ? description : be_name;
807 808
808 809 if (strcmp(title, new_title) == 0) {
809 810 ret = BE_SUCCESS;
810 811 goto cleanup;
811 812 } else {
812 813 if (be_remove_menu(be_name, be_root_pool,
813 814 boot_pool) != BE_SUCCESS) {
814 815 be_print_err(gettext("be_append_menu: "
815 816 "Failed to remove existing unusable "
816 817 "entry '%s' in boot menu.\n"), be_name);
817 818 ret = BE_ERR_BE_EXISTS;
818 819 goto cleanup;
819 820 }
820 821 }
821 822 }
822 823
823 824 /* Append BE entry to the end of the file */
824 825 menu_fp = fopen(menu_file, "a+");
825 826 err = errno;
826 827 if (menu_fp == NULL) {
827 828 be_print_err(gettext("be_append_menu: failed "
828 829 "to open menu.lst file %s\n"), menu_file);
829 830 ret = errno_to_be_err(err);
830 831 goto cleanup;
831 832 }
832 833
833 834 if (found_orig_be) {
834 835 /*
835 836 * write out all the stored lines
836 837 */
837 838 for (i = 0; i < num_lines; i++) {
838 839 (void) fprintf(menu_fp, "%s", entries[i]);
839 840 free(entries[i]);
840 841 }
841 842 num_lines = 0;
842 843
843 844 /*
844 845 * Check to see if this system supports grub
845 846 */
846 847 if (be_has_grub())
847 848 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
848 849 ret = BE_SUCCESS;
849 850 } else {
850 851 (void) fprintf(menu_fp, "title %s\n",
851 852 description ? description : be_name);
852 853 (void) fprintf(menu_fp, "bootfs %s\n", be_root_ds);
853 854
854 855 /*
855 856 * Check to see if this system supports grub
856 857 */
857 858 if (be_has_grub()) {
858 859 (void) fprintf(menu_fp, "kernel$ "
859 860 "/platform/i86pc/kernel/$ISADIR/unix -B "
860 861 "$ZFS-BOOTFS\n");
861 862 (void) fprintf(menu_fp, "module$ "
862 863 "/platform/i86pc/$ISADIR/boot_archive\n");
863 864 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
864 865 }
865 866 ret = BE_SUCCESS;
866 867 }
867 868 (void) fclose(menu_fp);
868 869 cleanup:
869 870 if (pool_mounted) {
870 871 int err = BE_SUCCESS;
871 872 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
872 873 if (ret == BE_SUCCESS)
873 874 ret = err;
874 875 free(orig_mntpnt);
875 876 free(ptmp_mntpnt);
876 877 }
877 878 ZFS_CLOSE(zhp);
878 879 if (num_tmp_lines > 0) {
879 880 for (i = 0; i < num_tmp_lines; i++) {
880 881 free(tmp_entries[i]);
881 882 tmp_entries[i] = NULL;
882 883 }
883 884 }
884 885 if (num_lines > 0) {
885 886 for (i = 0; i < num_lines; i++) {
886 887 free(entries[i]);
887 888 entries[i] = NULL;
888 889 }
889 890 }
890 891 return (ret);
891 892 }
892 893
893 894 /*
894 895 * Function: be_remove_menu
895 896 * Description: Removes a BE's entry from a menu.lst file.
896 897 * Parameters:
897 898 * be_name - the name of BE whose entry is to be removed from
898 899 * the menu.lst file.
899 900 * be_root_pool - the pool that be_name lives in.
900 901 * boot_pool - the pool where the BE is, if different than
901 902 * the pool containing the boot menu. If this is
902 903 * NULL it will be set to be_root_pool.
903 904 * Returns:
904 905 * BE_SUCCESS - Success
905 906 * be_errno_t - Failure
906 907 * Scope:
907 908 * Semi-private (library wide use only)
908 909 */
909 910 int
910 911 be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool)
911 912 {
912 913 zfs_handle_t *zhp = NULL;
913 914 char be_root_ds[MAXPATHLEN];
914 915 char **buffer = NULL;
915 916 char menu_buf[BUFSIZ];
916 917 char menu[MAXPATHLEN];
917 918 char *pool_mntpnt = NULL;
918 919 char *ptmp_mntpnt = NULL;
919 920 char *orig_mntpnt = NULL;
920 921 char *tmp_menu = NULL;
921 922 FILE *menu_fp = NULL;
922 923 FILE *tmp_menu_fp = NULL;
923 924 struct stat sb;
924 925 int ret = BE_SUCCESS;
925 926 int i;
926 927 int fd;
927 928 int err = 0;
928 929 int nlines = 0;
929 930 int default_entry = 0;
930 931 int entry_cnt = 0;
931 932 int entry_del = 0;
932 933 int num_entry_del = 0;
933 934 int tmp_menu_len = 0;
934 935 boolean_t write = B_TRUE;
935 936 boolean_t do_buffer = B_FALSE;
936 937 boolean_t pool_mounted = B_FALSE;
937 938
938 939 if (boot_pool == NULL)
939 940 boot_pool = be_root_pool;
940 941
941 942 /* Get name of BE's root dataset */
942 943 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
943 944
944 945 /* Get handle to pool dataset */
945 946 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
946 947 be_print_err(gettext("be_remove_menu: "
947 948 "failed to open pool dataset for %s: %s"),
948 949 be_root_pool, libzfs_error_description(g_zfs));
949 950 return (zfs_err_to_be_err(g_zfs));
950 951 }
951 952
952 953 /*
953 954 * Check to see if the pool's dataset is mounted. If it isn't we'll
954 955 * attempt to mount it.
955 956 */
956 957 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
957 958 &pool_mounted)) != BE_SUCCESS) {
958 959 be_print_err(gettext("be_remove_menu: pool dataset "
959 960 "(%s) could not be mounted\n"), be_root_pool);
960 961 ZFS_CLOSE(zhp);
961 962 return (ret);
962 963 }
963 964
964 965 /*
965 966 * Get the mountpoint for the root pool dataset.
966 967 */
967 968 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
968 969 be_print_err(gettext("be_remove_menu: pool "
969 970 "dataset (%s) is not mounted. Can't set "
970 971 "the default BE in the grub menu.\n"), be_root_pool);
971 972 ret = BE_ERR_NO_MENU;
972 973 goto cleanup;
973 974 }
974 975
975 976 /* Get path to boot menu */
976 977 (void) strlcpy(menu, pool_mntpnt, sizeof (menu));
977 978
978 979 /*
979 980 * Check to see if this system supports grub
980 981 */
981 982 if (be_has_grub())
982 983 (void) strlcat(menu, BE_GRUB_MENU, sizeof (menu));
983 984 else
984 985 (void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));
985 986
986 987 /* Get handle to boot menu file */
987 988 if ((ret = be_open_menu(be_root_pool, menu, &menu_fp, "r",
988 989 B_TRUE)) != BE_SUCCESS) {
989 990 goto cleanup;
990 991 } else if (menu_fp == NULL) {
991 992 ret = BE_ERR_NO_MENU;
992 993 goto cleanup;
993 994 }
994 995
995 996 free(pool_mntpnt);
996 997 pool_mntpnt = NULL;
997 998
998 999 /* Grab the stats of the original menu file */
999 1000 if (stat(menu, &sb) != 0) {
1000 1001 err = errno;
1001 1002 be_print_err(gettext("be_remove_menu: "
1002 1003 "failed to stat file %s: %s\n"), menu, strerror(err));
1003 1004 ret = errno_to_be_err(err);
1004 1005 goto cleanup;
1005 1006 }
1006 1007
1007 1008 /* Create a tmp file for the modified menu.lst */
1008 1009 tmp_menu_len = strlen(menu) + 7;
1009 1010 if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) {
1010 1011 be_print_err(gettext("be_remove_menu: malloc failed\n"));
1011 1012 ret = BE_ERR_NOMEM;
1012 1013 goto cleanup;
1013 1014 }
1014 1015 (void) memset(tmp_menu, 0, tmp_menu_len);
1015 1016 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
1016 1017 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1017 1018 if ((fd = mkstemp(tmp_menu)) == -1) {
1018 1019 err = errno;
1019 1020 be_print_err(gettext("be_remove_menu: mkstemp failed\n"));
1020 1021 ret = errno_to_be_err(err);
1021 1022 free(tmp_menu);
1022 1023 tmp_menu = NULL;
1023 1024 goto cleanup;
1024 1025 }
1025 1026 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1026 1027 err = errno;
1027 1028 be_print_err(gettext("be_remove_menu: "
1028 1029 "could not open tmp file for write: %s\n"), strerror(err));
1029 1030 (void) close(fd);
1030 1031 ret = errno_to_be_err(err);
1031 1032 goto cleanup;
1032 1033 }
1033 1034
1034 1035 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1035 1036 char tline [BUFSIZ];
1036 1037 char *tok = NULL;
1037 1038
1038 1039 (void) strlcpy(tline, menu_buf, sizeof (tline));
1039 1040
1040 1041 /* Tokenize line */
1041 1042 tok = strtok(tline, BE_WHITE_SPACE);
1042 1043
1043 1044 if (tok == NULL || tok[0] == '#') {
1044 1045 /* Found empty line or comment line */
1045 1046 if (do_buffer) {
1046 1047 /* Buffer this line */
1047 1048 if ((buffer = (char **)realloc(buffer,
1048 1049 sizeof (char *)*(nlines + 1))) == NULL) {
1049 1050 ret = BE_ERR_NOMEM;
1050 1051 goto cleanup;
1051 1052 }
1052 1053 if ((buffer[nlines++] = strdup(menu_buf))
1053 1054 == NULL) {
1054 1055 ret = BE_ERR_NOMEM;
1055 1056 goto cleanup;
1056 1057 }
1057 1058
1058 1059 } else if (write || strncmp(menu_buf, BE_GRUB_COMMENT,
1059 1060 strlen(BE_GRUB_COMMENT)) != 0) {
1060 1061 /* Write this line out */
1061 1062 (void) fputs(menu_buf, tmp_menu_fp);
1062 1063 }
1063 1064 } else if (strcmp(tok, "default") == 0) {
1064 1065 /*
1065 1066 * Record what 'default' is set to because we might
1066 1067 * need to adjust this upon deleting an entry.
1067 1068 */
1068 1069 tok = strtok(NULL, BE_WHITE_SPACE);
1069 1070
1070 1071 if (tok != NULL) {
1071 1072 default_entry = atoi(tok);
1072 1073 }
1073 1074
1074 1075 (void) fputs(menu_buf, tmp_menu_fp);
1075 1076 } else if (strcmp(tok, "title") == 0) {
1076 1077 /*
1077 1078 * If we've reached a 'title' line and do_buffer is
1078 1079 * is true, that means we've just buffered an entire
1079 1080 * entry without finding a 'bootfs' directive. We
1080 1081 * need to write that entry out and keep searching.
1081 1082 */
1082 1083 if (do_buffer) {
1083 1084 for (i = 0; i < nlines; i++) {
1084 1085 (void) fputs(buffer[i], tmp_menu_fp);
1085 1086 free(buffer[i]);
1086 1087 }
1087 1088 free(buffer);
1088 1089 buffer = NULL;
1089 1090 nlines = 0;
1090 1091 }
1091 1092
1092 1093 /*
1093 1094 * Turn writing off and buffering on, and increment
1094 1095 * our entry counter.
1095 1096 */
1096 1097 write = B_FALSE;
1097 1098 do_buffer = B_TRUE;
1098 1099 entry_cnt++;
1099 1100
1100 1101 /* Buffer this 'title' line */
1101 1102 if ((buffer = (char **)realloc(buffer,
1102 1103 sizeof (char *)*(nlines + 1))) == NULL) {
1103 1104 ret = BE_ERR_NOMEM;
1104 1105 goto cleanup;
1105 1106 }
1106 1107 if ((buffer[nlines++] = strdup(menu_buf)) == NULL) {
1107 1108 ret = BE_ERR_NOMEM;
1108 1109 goto cleanup;
1109 1110 }
1110 1111
1111 1112 } else if (strcmp(tok, "bootfs") == 0) {
1112 1113 char *bootfs = NULL;
1113 1114
1114 1115 /*
1115 1116 * Found a 'bootfs' line. See if it matches the
1116 1117 * BE we're looking for.
1117 1118 */
1118 1119 if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
1119 1120 strcmp(bootfs, be_root_ds) != 0) {
1120 1121 /*
1121 1122 * Either there's nothing after the 'bootfs'
1122 1123 * or this is not the BE we're looking for,
1123 1124 * write out the line(s) we've buffered since
1124 1125 * finding the title.
1125 1126 */
1126 1127 for (i = 0; i < nlines; i++) {
1127 1128 (void) fputs(buffer[i], tmp_menu_fp);
1128 1129 free(buffer[i]);
1129 1130 }
1130 1131 free(buffer);
1131 1132 buffer = NULL;
1132 1133 nlines = 0;
1133 1134
1134 1135 /*
1135 1136 * Turn writing back on, and turn off buffering
1136 1137 * since this isn't the entry we're looking
1137 1138 * for.
1138 1139 */
1139 1140 write = B_TRUE;
1140 1141 do_buffer = B_FALSE;
1141 1142
1142 1143 /* Write this 'bootfs' line out. */
1143 1144 (void) fputs(menu_buf, tmp_menu_fp);
1144 1145 } else {
1145 1146 /*
1146 1147 * Found the entry we're looking for.
1147 1148 * Record its entry number, increment the
1148 1149 * number of entries we've deleted, and turn
1149 1150 * writing off. Also, throw away the lines
1150 1151 * we've buffered for this entry so far, we
1151 1152 * don't need them.
1152 1153 */
1153 1154 entry_del = entry_cnt - 1;
1154 1155 num_entry_del++;
1155 1156 write = B_FALSE;
1156 1157 do_buffer = B_FALSE;
1157 1158
1158 1159 for (i = 0; i < nlines; i++) {
1159 1160 free(buffer[i]);
1160 1161 }
1161 1162 free(buffer);
1162 1163 buffer = NULL;
1163 1164 nlines = 0;
1164 1165 }
1165 1166 } else {
1166 1167 if (do_buffer) {
1167 1168 /* Buffer this line */
1168 1169 if ((buffer = (char **)realloc(buffer,
1169 1170 sizeof (char *)*(nlines + 1))) == NULL) {
1170 1171 ret = BE_ERR_NOMEM;
1171 1172 goto cleanup;
1172 1173 }
1173 1174 if ((buffer[nlines++] = strdup(menu_buf))
1174 1175 == NULL) {
1175 1176 ret = BE_ERR_NOMEM;
1176 1177 goto cleanup;
1177 1178 }
1178 1179 } else if (write) {
1179 1180 /* Write this line out */
1180 1181 (void) fputs(menu_buf, tmp_menu_fp);
1181 1182 }
1182 1183 }
1183 1184 }
1184 1185
1185 1186 (void) fclose(menu_fp);
1186 1187 menu_fp = NULL;
1187 1188 (void) fclose(tmp_menu_fp);
1188 1189 tmp_menu_fp = NULL;
1189 1190
1190 1191 /* Copy the modified menu.lst into place */
1191 1192 if (rename(tmp_menu, menu) != 0) {
1192 1193 err = errno;
1193 1194 be_print_err(gettext("be_remove_menu: "
1194 1195 "failed to rename file %s to %s: %s\n"),
1195 1196 tmp_menu, menu, strerror(err));
1196 1197 ret = errno_to_be_err(err);
1197 1198 goto cleanup;
1198 1199 }
1199 1200 free(tmp_menu);
1200 1201 tmp_menu = NULL;
1201 1202
1202 1203 /*
1203 1204 * If we've removed an entry, see if we need to
1204 1205 * adjust the default value in the menu.lst. If the
1205 1206 * entry we've deleted comes before the default entry
1206 1207 * we need to adjust the default value accordingly.
1207 1208 *
1208 1209 * be_has_grub is used here to check to see if this system
1209 1210 * supports grub.
1210 1211 */
1211 1212 if (be_has_grub() && num_entry_del > 0) {
1212 1213 if (entry_del <= default_entry) {
1213 1214 default_entry = default_entry - num_entry_del;
1214 1215 if (default_entry < 0)
1215 1216 default_entry = 0;
1216 1217
1217 1218 /*
1218 1219 * Adjust the default value by rewriting the
1219 1220 * menu.lst file. This may be overkill, but to
1220 1221 * preserve the location of the 'default' entry
1221 1222 * in the file, we need to do this.
1222 1223 */
1223 1224
1224 1225 /* Get handle to boot menu file */
1225 1226 if ((menu_fp = fopen(menu, "r")) == NULL) {
1226 1227 err = errno;
1227 1228 be_print_err(gettext("be_remove_menu: "
1228 1229 "failed to open menu.lst (%s): %s\n"),
1229 1230 menu, strerror(err));
1230 1231 ret = errno_to_be_err(err);
1231 1232 goto cleanup;
1232 1233 }
1233 1234
1234 1235 /* Create a tmp file for the modified menu.lst */
1235 1236 tmp_menu_len = strlen(menu) + 7;
1236 1237 if ((tmp_menu = (char *)malloc(tmp_menu_len))
1237 1238 == NULL) {
1238 1239 be_print_err(gettext("be_remove_menu: "
1239 1240 "malloc failed\n"));
1240 1241 ret = BE_ERR_NOMEM;
1241 1242 goto cleanup;
1242 1243 }
1243 1244 (void) memset(tmp_menu, 0, tmp_menu_len);
1244 1245 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
1245 1246 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1246 1247 if ((fd = mkstemp(tmp_menu)) == -1) {
1247 1248 err = errno;
1248 1249 be_print_err(gettext("be_remove_menu: "
1249 1250 "mkstemp failed: %s\n"), strerror(err));
1250 1251 ret = errno_to_be_err(err);
1251 1252 free(tmp_menu);
1252 1253 tmp_menu = NULL;
1253 1254 goto cleanup;
1254 1255 }
1255 1256 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1256 1257 err = errno;
1257 1258 be_print_err(gettext("be_remove_menu: "
1258 1259 "could not open tmp file for write: %s\n"),
1259 1260 strerror(err));
1260 1261 (void) close(fd);
1261 1262 ret = errno_to_be_err(err);
1262 1263 goto cleanup;
1263 1264 }
1264 1265
1265 1266 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1266 1267 char tline [BUFSIZ];
1267 1268 char *tok = NULL;
1268 1269
1269 1270 (void) strlcpy(tline, menu_buf, sizeof (tline));
1270 1271
1271 1272 /* Tokenize line */
1272 1273 tok = strtok(tline, BE_WHITE_SPACE);
1273 1274
1274 1275 if (tok == NULL) {
1275 1276 /* Found empty line, write it out */
1276 1277 (void) fputs(menu_buf, tmp_menu_fp);
1277 1278 } else if (strcmp(tok, "default") == 0) {
1278 1279 /* Found the default line, adjust it */
1279 1280 (void) snprintf(tline, sizeof (tline),
1280 1281 "default %d\n", default_entry);
1281 1282
1282 1283 (void) fputs(tline, tmp_menu_fp);
1283 1284 } else {
1284 1285 /* Pass through all other lines */
1285 1286 (void) fputs(menu_buf, tmp_menu_fp);
1286 1287 }
1287 1288 }
1288 1289
1289 1290 (void) fclose(menu_fp);
1290 1291 menu_fp = NULL;
1291 1292 (void) fclose(tmp_menu_fp);
1292 1293 tmp_menu_fp = NULL;
1293 1294
1294 1295 /* Copy the modified menu.lst into place */
1295 1296 if (rename(tmp_menu, menu) != 0) {
1296 1297 err = errno;
1297 1298 be_print_err(gettext("be_remove_menu: "
1298 1299 "failed to rename file %s to %s: %s\n"),
1299 1300 tmp_menu, menu, strerror(err));
1300 1301 ret = errno_to_be_err(err);
1301 1302 goto cleanup;
1302 1303 }
1303 1304
1304 1305 free(tmp_menu);
1305 1306 tmp_menu = NULL;
1306 1307 }
1307 1308 }
1308 1309
1309 1310 /* Set the perms and ownership of the updated file */
1310 1311 if (chmod(menu, sb.st_mode) != 0) {
1311 1312 err = errno;
1312 1313 be_print_err(gettext("be_remove_menu: "
1313 1314 "failed to chmod %s: %s\n"), menu, strerror(err));
1314 1315 ret = errno_to_be_err(err);
1315 1316 goto cleanup;
1316 1317 }
1317 1318 if (chown(menu, sb.st_uid, sb.st_gid) != 0) {
1318 1319 err = errno;
1319 1320 be_print_err(gettext("be_remove_menu: "
1320 1321 "failed to chown %s: %s\n"), menu, strerror(err));
1321 1322 ret = errno_to_be_err(err);
1322 1323 goto cleanup;
1323 1324 }
1324 1325
1325 1326 cleanup:
1326 1327 if (pool_mounted) {
1327 1328 int err = BE_SUCCESS;
1328 1329 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1329 1330 if (ret == BE_SUCCESS)
1330 1331 ret = err;
1331 1332 free(orig_mntpnt);
1332 1333 free(ptmp_mntpnt);
1333 1334 }
1334 1335 ZFS_CLOSE(zhp);
1335 1336
1336 1337 free(buffer);
1337 1338 if (menu_fp != NULL)
1338 1339 (void) fclose(menu_fp);
1339 1340 if (tmp_menu_fp != NULL)
1340 1341 (void) fclose(tmp_menu_fp);
1341 1342 if (tmp_menu != NULL) {
1342 1343 (void) unlink(tmp_menu);
1343 1344 free(tmp_menu);
1344 1345 }
1345 1346
1346 1347 return (ret);
1347 1348 }
1348 1349
1349 1350 /*
1350 1351 * Function: be_default_grub_bootfs
1351 1352 * Description: This function returns the dataset in the default entry of
1352 1353 * the grub menu. If no default entry is found with a valid bootfs
1353 1354 * entry NULL is returned.
1354 1355 * Parameters:
1355 1356 * be_root_pool - This is the name of the root pool where the
1356 1357 * grub menu can be found.
1357 1358 * def_bootfs - This is used to pass back the bootfs string. On
1358 1359 * error NULL is returned here.
1359 1360 * Returns:
1360 1361 * Success - BE_SUCCESS is returned.
1361 1362 * Failure - a be_errno_t is returned.
1362 1363 * Scope:
1363 1364 * Semi-private (library wide use only)
1364 1365 */
1365 1366 int
1366 1367 be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs)
1367 1368 {
1368 1369 zfs_handle_t *zhp = NULL;
1369 1370 char grub_file[MAXPATHLEN];
1370 1371 FILE *menu_fp;
1371 1372 char line[BUFSIZ];
1372 1373 char *pool_mntpnt = NULL;
1373 1374 char *ptmp_mntpnt = NULL;
1374 1375 char *orig_mntpnt = NULL;
1375 1376 int default_entry = 0, entries = 0;
1376 1377 int found_default = 0;
1377 1378 int ret = BE_SUCCESS;
1378 1379 boolean_t pool_mounted = B_FALSE;
1379 1380
1380 1381 errno = 0;
1381 1382
1382 1383 /*
1383 1384 * Check to see if this system supports grub
1384 1385 */
1385 1386 if (!be_has_grub()) {
1386 1387 be_print_err(gettext("be_default_grub_bootfs: operation "
1387 1388 "not supported on this architecture\n"));
1388 1389 return (BE_ERR_NOTSUP);
1389 1390 }
1390 1391
1391 1392 *def_bootfs = NULL;
1392 1393
1393 1394 /* Get handle to pool dataset */
1394 1395 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1395 1396 be_print_err(gettext("be_default_grub_bootfs: "
1396 1397 "failed to open pool dataset for %s: %s"),
1397 1398 be_root_pool, libzfs_error_description(g_zfs));
1398 1399 return (zfs_err_to_be_err(g_zfs));
1399 1400 }
1400 1401
1401 1402 /*
1402 1403 * Check to see if the pool's dataset is mounted. If it isn't we'll
1403 1404 * attempt to mount it.
1404 1405 */
1405 1406 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1406 1407 &pool_mounted)) != BE_SUCCESS) {
1407 1408 be_print_err(gettext("be_default_grub_bootfs: pool dataset "
1408 1409 "(%s) could not be mounted\n"), be_root_pool);
1409 1410 ZFS_CLOSE(zhp);
1410 1411 return (ret);
1411 1412 }
1412 1413
1413 1414 /*
1414 1415 * Get the mountpoint for the root pool dataset.
1415 1416 */
1416 1417 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1417 1418 be_print_err(gettext("be_default_grub_bootfs: failed "
1418 1419 "to get mount point for the root pool. Can't set "
1419 1420 "the default BE in the grub menu.\n"));
1420 1421 ret = BE_ERR_NO_MENU;
1421 1422 goto cleanup;
1422 1423 }
1423 1424
1424 1425 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1425 1426 pool_mntpnt, BE_GRUB_MENU);
1426 1427
1427 1428 if ((ret = be_open_menu((char *)be_root_pool, grub_file,
1428 1429 &menu_fp, "r", B_FALSE)) != BE_SUCCESS) {
1429 1430 goto cleanup;
1430 1431 } else if (menu_fp == NULL) {
1431 1432 ret = BE_ERR_NO_MENU;
1432 1433 goto cleanup;
1433 1434 }
1434 1435
1435 1436 free(pool_mntpnt);
1436 1437 pool_mntpnt = NULL;
1437 1438
1438 1439 while (fgets(line, BUFSIZ, menu_fp)) {
1439 1440 char *tok = strtok(line, BE_WHITE_SPACE);
1440 1441
1441 1442 if (tok != NULL && tok[0] != '#') {
1442 1443 if (!found_default) {
1443 1444 if (strcmp(tok, "default") == 0) {
1444 1445 tok = strtok(NULL, BE_WHITE_SPACE);
1445 1446 if (tok != NULL) {
1446 1447 default_entry = atoi(tok);
1447 1448 rewind(menu_fp);
1448 1449 found_default = 1;
1449 1450 }
1450 1451 }
1451 1452 continue;
1452 1453 }
1453 1454 if (strcmp(tok, "title") == 0) {
1454 1455 entries++;
1455 1456 } else if (default_entry == entries - 1) {
1456 1457 if (strcmp(tok, "bootfs") == 0) {
1457 1458 tok = strtok(NULL, BE_WHITE_SPACE);
1458 1459 (void) fclose(menu_fp);
1459 1460
1460 1461 if (tok == NULL) {
1461 1462 ret = BE_SUCCESS;
1462 1463 goto cleanup;
1463 1464 }
1464 1465
1465 1466 if ((*def_bootfs = strdup(tok)) !=
1466 1467 NULL) {
1467 1468 ret = BE_SUCCESS;
1468 1469 goto cleanup;
1469 1470 }
1470 1471 be_print_err(gettext(
1471 1472 "be_default_grub_bootfs: "
1472 1473 "memory allocation failed\n"));
1473 1474 ret = BE_ERR_NOMEM;
1474 1475 goto cleanup;
1475 1476 }
1476 1477 } else if (default_entry < entries - 1) {
1477 1478 /*
1478 1479 * no bootfs entry for the default entry.
1479 1480 */
1480 1481 break;
1481 1482 }
1482 1483 }
1483 1484 }
1484 1485 (void) fclose(menu_fp);
1485 1486
1486 1487 cleanup:
1487 1488 if (pool_mounted) {
1488 1489 int err = BE_SUCCESS;
1489 1490 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1490 1491 if (ret == BE_SUCCESS)
1491 1492 ret = err;
1492 1493 free(orig_mntpnt);
1493 1494 free(ptmp_mntpnt);
1494 1495 }
1495 1496 ZFS_CLOSE(zhp);
1496 1497 return (ret);
1497 1498 }
1498 1499
1499 1500 /*
1500 1501 * Function: be_change_grub_default
1501 1502 * Description: This function takes two parameters. These are the name of
1502 1503 * the BE we want to have as the default booted in the grub
1503 1504 * menu and the root pool where the path to the grub menu exists.
1504 1505 * The code takes this and finds the BE's entry in the grub menu
1505 1506 * and changes the default entry to point to that entry in the
1506 1507 * list.
1507 1508 * Parameters:
1508 1509 * be_name - This is the name of the BE wanted as the default
1509 1510 * for the next boot.
1510 1511 * be_root_pool - This is the name of the root pool where the
1511 1512 * grub menu can be found.
1512 1513 * Returns:
1513 1514 * BE_SUCCESS - Success
1514 1515 * be_errno_t - Failure
1515 1516 * Scope:
1516 1517 * Semi-private (library wide use only)
1517 1518 */
1518 1519 int
1519 1520 be_change_grub_default(char *be_name, char *be_root_pool)
1520 1521 {
1521 1522 zfs_handle_t *zhp = NULL;
1522 1523 char grub_file[MAXPATHLEN];
1523 1524 char *temp_grub = NULL;
1524 1525 char *pool_mntpnt = NULL;
1525 1526 char *ptmp_mntpnt = NULL;
1526 1527 char *orig_mntpnt = NULL;
1527 1528 char line[BUFSIZ];
1528 1529 char temp_line[BUFSIZ];
1529 1530 char be_root_ds[MAXPATHLEN];
1530 1531 FILE *grub_fp = NULL;
1531 1532 FILE *temp_fp = NULL;
1532 1533 struct stat sb;
1533 1534 int temp_grub_len = 0;
1534 1535 int fd, entries = 0;
1535 1536 int err = 0;
1536 1537 int ret = BE_SUCCESS;
1537 1538 boolean_t found_default = B_FALSE;
1538 1539 boolean_t pool_mounted = B_FALSE;
1539 1540
1540 1541 errno = 0;
1541 1542
1542 1543 /*
1543 1544 * Check to see if this system supports grub
1544 1545 */
1545 1546 if (!be_has_grub()) {
1546 1547 be_print_err(gettext("be_change_grub_default: operation "
1547 1548 "not supported on this architecture\n"));
1548 1549 return (BE_ERR_NOTSUP);
1549 1550 }
1550 1551
1551 1552 /* Generate string for BE's root dataset */
1552 1553 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
1553 1554
1554 1555 /* Get handle to pool dataset */
1555 1556 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1556 1557 be_print_err(gettext("be_change_grub_default: "
1557 1558 "failed to open pool dataset for %s: %s"),
1558 1559 be_root_pool, libzfs_error_description(g_zfs));
1559 1560 return (zfs_err_to_be_err(g_zfs));
1560 1561 }
1561 1562
1562 1563 /*
1563 1564 * Check to see if the pool's dataset is mounted. If it isn't we'll
1564 1565 * attempt to mount it.
1565 1566 */
1566 1567 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1567 1568 &pool_mounted)) != BE_SUCCESS) {
1568 1569 be_print_err(gettext("be_change_grub_default: pool dataset "
1569 1570 "(%s) could not be mounted\n"), be_root_pool);
1570 1571 ZFS_CLOSE(zhp);
1571 1572 return (ret);
1572 1573 }
1573 1574
1574 1575 /*
1575 1576 * Get the mountpoint for the root pool dataset.
1576 1577 */
1577 1578 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1578 1579 be_print_err(gettext("be_change_grub_default: pool "
1579 1580 "dataset (%s) is not mounted. Can't set "
1580 1581 "the default BE in the grub menu.\n"), be_root_pool);
1581 1582 ret = BE_ERR_NO_MENU;
1582 1583 goto cleanup;
1583 1584 }
1584 1585
1585 1586 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1586 1587 pool_mntpnt, BE_GRUB_MENU);
1587 1588
1588 1589 if ((ret = be_open_menu(be_root_pool, grub_file,
1589 1590 &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) {
1590 1591 goto cleanup;
1591 1592 } else if (grub_fp == NULL) {
1592 1593 ret = BE_ERR_NO_MENU;
1593 1594 goto cleanup;
1594 1595 }
1595 1596
1596 1597 free(pool_mntpnt);
1597 1598 pool_mntpnt = NULL;
1598 1599
1599 1600 /* Grab the stats of the original menu file */
1600 1601 if (stat(grub_file, &sb) != 0) {
1601 1602 err = errno;
1602 1603 be_print_err(gettext("be_change_grub_default: "
1603 1604 "failed to stat file %s: %s\n"), grub_file, strerror(err));
1604 1605 ret = errno_to_be_err(err);
1605 1606 goto cleanup;
1606 1607 }
1607 1608
1608 1609 /* Create a tmp file for the modified menu.lst */
1609 1610 temp_grub_len = strlen(grub_file) + 7;
1610 1611 if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) {
1611 1612 be_print_err(gettext("be_change_grub_default: "
1612 1613 "malloc failed\n"));
1613 1614 ret = BE_ERR_NOMEM;
1614 1615 goto cleanup;
1615 1616 }
1616 1617 (void) memset(temp_grub, 0, temp_grub_len);
1617 1618 (void) strlcpy(temp_grub, grub_file, temp_grub_len);
1618 1619 (void) strlcat(temp_grub, "XXXXXX", temp_grub_len);
1619 1620 if ((fd = mkstemp(temp_grub)) == -1) {
1620 1621 err = errno;
1621 1622 be_print_err(gettext("be_change_grub_default: "
1622 1623 "mkstemp failed: %s\n"), strerror(err));
1623 1624 ret = errno_to_be_err(err);
1624 1625 free(temp_grub);
1625 1626 temp_grub = NULL;
1626 1627 goto cleanup;
1627 1628 }
1628 1629 if ((temp_fp = fdopen(fd, "w")) == NULL) {
1629 1630 err = errno;
1630 1631 be_print_err(gettext("be_change_grub_default: "
1631 1632 "failed to open %s file: %s\n"),
1632 1633 temp_grub, strerror(err));
1633 1634 (void) close(fd);
1634 1635 ret = errno_to_be_err(err);
1635 1636 goto cleanup;
1636 1637 }
1637 1638
1638 1639 while (fgets(line, BUFSIZ, grub_fp)) {
1639 1640 char *tok = strtok(line, BE_WHITE_SPACE);
1640 1641
1641 1642 if (tok == NULL || tok[0] == '#') {
1642 1643 continue;
1643 1644 } else if (strcmp(tok, "title") == 0) {
1644 1645 entries++;
1645 1646 continue;
1646 1647 } else if (strcmp(tok, "bootfs") == 0) {
1647 1648 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
1648 1649 if (bootfs == NULL)
1649 1650 continue;
1650 1651
1651 1652 if (strcmp(bootfs, be_root_ds) == 0) {
1652 1653 found_default = B_TRUE;
1653 1654 break;
1654 1655 }
1655 1656 }
1656 1657 }
1657 1658
1658 1659 if (!found_default) {
1659 1660 be_print_err(gettext("be_change_grub_default: failed "
1660 1661 "to find entry for %s in the grub menu\n"),
1661 1662 be_name);
1662 1663 ret = BE_ERR_BE_NOENT;
1663 1664 goto cleanup;
1664 1665 }
1665 1666
1666 1667 rewind(grub_fp);
1667 1668
1668 1669 while (fgets(line, BUFSIZ, grub_fp)) {
1669 1670 char *tok = NULL;
1670 1671
1671 1672 (void) strncpy(temp_line, line, BUFSIZ);
1672 1673
1673 1674 if ((tok = strtok(temp_line, BE_WHITE_SPACE)) != NULL &&
1674 1675 strcmp(tok, "default") == 0) {
1675 1676 (void) snprintf(temp_line, BUFSIZ, "default %d\n",
1676 1677 entries - 1 >= 0 ? entries - 1 : 0);
1677 1678 (void) fputs(temp_line, temp_fp);
1678 1679 } else {
1679 1680 (void) fputs(line, temp_fp);
1680 1681 }
1681 1682 }
1682 1683
1683 1684 (void) fclose(grub_fp);
1684 1685 grub_fp = NULL;
1685 1686 (void) fclose(temp_fp);
1686 1687 temp_fp = NULL;
1687 1688
1688 1689 if (rename(temp_grub, grub_file) != 0) {
1689 1690 err = errno;
1690 1691 be_print_err(gettext("be_change_grub_default: "
1691 1692 "failed to rename file %s to %s: %s\n"),
1692 1693 temp_grub, grub_file, strerror(err));
1693 1694 ret = errno_to_be_err(err);
1694 1695 goto cleanup;
1695 1696 }
1696 1697 free(temp_grub);
1697 1698 temp_grub = NULL;
1698 1699
1699 1700 /* Set the perms and ownership of the updated file */
1700 1701 if (chmod(grub_file, sb.st_mode) != 0) {
1701 1702 err = errno;
1702 1703 be_print_err(gettext("be_change_grub_default: "
1703 1704 "failed to chmod %s: %s\n"), grub_file, strerror(err));
1704 1705 ret = errno_to_be_err(err);
1705 1706 goto cleanup;
1706 1707 }
1707 1708 if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) {
1708 1709 err = errno;
1709 1710 be_print_err(gettext("be_change_grub_default: "
1710 1711 "failed to chown %s: %s\n"), grub_file, strerror(err));
1711 1712 ret = errno_to_be_err(err);
1712 1713 }
1713 1714
1714 1715 cleanup:
1715 1716 if (pool_mounted) {
1716 1717 int err = BE_SUCCESS;
1717 1718 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1718 1719 if (ret == BE_SUCCESS)
1719 1720 ret = err;
1720 1721 free(orig_mntpnt);
1721 1722 free(ptmp_mntpnt);
1722 1723 }
1723 1724 ZFS_CLOSE(zhp);
1724 1725 if (grub_fp != NULL)
1725 1726 (void) fclose(grub_fp);
1726 1727 if (temp_fp != NULL)
1727 1728 (void) fclose(temp_fp);
1728 1729 if (temp_grub != NULL) {
1729 1730 (void) unlink(temp_grub);
1730 1731 free(temp_grub);
1731 1732 }
1732 1733
1733 1734 return (ret);
1734 1735 }
1735 1736
1736 1737 /*
1737 1738 * Function: be_update_menu
1738 1739 * Description: This function is used by be_rename to change the BE name in
1739 1740 * an existing entry in the grub menu to the new name of the BE.
1740 1741 * Parameters:
1741 1742 * be_orig_name - the original name of the BE
1742 1743 * be_new_name - the new name the BE is being renameed to.
1743 1744 * be_root_pool - The pool which contains the grub menu
1744 1745 * boot_pool - the pool where the BE is, if different than
1745 1746 * the pool containing the boot menu. If this is
1746 1747 * NULL it will be set to be_root_pool.
1747 1748 * Returns:
1748 1749 * BE_SUCCESS - Success
1749 1750 * be_errno_t - Failure
1750 1751 * Scope:
1751 1752 * Semi-private (library wide use only)
1752 1753 */
1753 1754 int
1754 1755 be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool,
1755 1756 char *boot_pool)
1756 1757 {
1757 1758 zfs_handle_t *zhp = NULL;
1758 1759 char menu_file[MAXPATHLEN];
1759 1760 char be_root_ds[MAXPATHLEN];
1760 1761 char be_new_root_ds[MAXPATHLEN];
1761 1762 char line[BUFSIZ];
1762 1763 char *pool_mntpnt = NULL;
1763 1764 char *ptmp_mntpnt = NULL;
1764 1765 char *orig_mntpnt = NULL;
1765 1766 char *temp_menu = NULL;
1766 1767 FILE *menu_fp = NULL;
1767 1768 FILE *new_fp = NULL;
1768 1769 struct stat sb;
1769 1770 int temp_menu_len = 0;
1770 1771 int tmp_fd;
1771 1772 int ret = BE_SUCCESS;
1772 1773 int err = 0;
1773 1774 boolean_t pool_mounted = B_FALSE;
1774 1775
1775 1776 errno = 0;
1776 1777
1777 1778 if (boot_pool == NULL)
1778 1779 boot_pool = be_root_pool;
1779 1780
1780 1781 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1781 1782 be_print_err(gettext("be_update_menu: failed to open "
1782 1783 "pool dataset for %s: %s\n"), be_root_pool,
1783 1784 libzfs_error_description(g_zfs));
1784 1785 return (zfs_err_to_be_err(g_zfs));
1785 1786 }
1786 1787
1787 1788 /*
1788 1789 * Check to see if the pool's dataset is mounted. If it isn't we'll
1789 1790 * attempt to mount it.
1790 1791 */
1791 1792 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1792 1793 &pool_mounted)) != BE_SUCCESS) {
1793 1794 be_print_err(gettext("be_update_menu: pool dataset "
1794 1795 "(%s) could not be mounted\n"), be_root_pool);
1795 1796 ZFS_CLOSE(zhp);
1796 1797 return (ret);
1797 1798 }
1798 1799
1799 1800 /*
1800 1801 * Get the mountpoint for the root pool dataset.
1801 1802 */
1802 1803 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1803 1804 be_print_err(gettext("be_update_menu: failed "
1804 1805 "to get mount point for the root pool. Can't set "
1805 1806 "the default BE in the grub menu.\n"));
1806 1807 ret = BE_ERR_NO_MENU;
1807 1808 goto cleanup;
1808 1809 }
1809 1810
1810 1811 /*
1811 1812 * Check to see if this system supports grub
1812 1813 */
1813 1814 if (be_has_grub()) {
1814 1815 (void) snprintf(menu_file, sizeof (menu_file),
1815 1816 "%s%s", pool_mntpnt, BE_GRUB_MENU);
1816 1817 } else {
1817 1818 (void) snprintf(menu_file, sizeof (menu_file),
1818 1819 "%s%s", pool_mntpnt, BE_SPARC_MENU);
1819 1820 }
1820 1821
1821 1822 be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
1822 1823 sizeof (be_root_ds));
1823 1824 be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
1824 1825 sizeof (be_new_root_ds));
1825 1826
1826 1827 if ((ret = be_open_menu(be_root_pool, menu_file,
1827 1828 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
1828 1829 goto cleanup;
1829 1830 } else if (menu_fp == NULL) {
1830 1831 ret = BE_ERR_NO_MENU;
1831 1832 goto cleanup;
1832 1833 }
1833 1834
1834 1835 free(pool_mntpnt);
1835 1836 pool_mntpnt = NULL;
1836 1837
1837 1838 /* Grab the stat of the original menu file */
1838 1839 if (stat(menu_file, &sb) != 0) {
1839 1840 err = errno;
1840 1841 be_print_err(gettext("be_update_menu: "
1841 1842 "failed to stat file %s: %s\n"), menu_file, strerror(err));
1842 1843 (void) fclose(menu_fp);
1843 1844 ret = errno_to_be_err(err);
1844 1845 goto cleanup;
1845 1846 }
1846 1847
1847 1848 /* Create tmp file for modified menu.lst */
1848 1849 temp_menu_len = strlen(menu_file) + 7;
1849 1850 if ((temp_menu = (char *)malloc(temp_menu_len))
1850 1851 == NULL) {
1851 1852 be_print_err(gettext("be_update_menu: "
1852 1853 "malloc failed\n"));
1853 1854 (void) fclose(menu_fp);
1854 1855 ret = BE_ERR_NOMEM;
1855 1856 goto cleanup;
1856 1857 }
1857 1858 (void) memset(temp_menu, 0, temp_menu_len);
1858 1859 (void) strlcpy(temp_menu, menu_file, temp_menu_len);
1859 1860 (void) strlcat(temp_menu, "XXXXXX", temp_menu_len);
1860 1861 if ((tmp_fd = mkstemp(temp_menu)) == -1) {
1861 1862 err = errno;
1862 1863 be_print_err(gettext("be_update_menu: "
1863 1864 "mkstemp failed: %s\n"), strerror(err));
1864 1865 (void) fclose(menu_fp);
1865 1866 free(temp_menu);
1866 1867 ret = errno_to_be_err(err);
1867 1868 goto cleanup;
1868 1869 }
1869 1870 if ((new_fp = fdopen(tmp_fd, "w")) == NULL) {
1870 1871 err = errno;
1871 1872 be_print_err(gettext("be_update_menu: "
1872 1873 "fdopen failed: %s\n"), strerror(err));
1873 1874 (void) close(tmp_fd);
1874 1875 (void) fclose(menu_fp);
1875 1876 free(temp_menu);
1876 1877 ret = errno_to_be_err(err);
1877 1878 goto cleanup;
1878 1879 }
1879 1880
1880 1881 while (fgets(line, BUFSIZ, menu_fp)) {
1881 1882 char tline[BUFSIZ];
1882 1883 char new_line[BUFSIZ];
1883 1884 char *c = NULL;
1884 1885
1885 1886 (void) strlcpy(tline, line, sizeof (tline));
1886 1887
1887 1888 /* Tokenize line */
1888 1889 c = strtok(tline, BE_WHITE_SPACE);
1889 1890
1890 1891 if (c == NULL) {
1891 1892 /* Found empty line, write it out. */
1892 1893 (void) fputs(line, new_fp);
1893 1894 } else if (c[0] == '#') {
1894 1895 /* Found a comment line, write it out. */
1895 1896 (void) fputs(line, new_fp);
1896 1897 } else if (strcmp(c, "title") == 0) {
1897 1898 char *name = NULL;
1898 1899 char *desc = NULL;
1899 1900
1900 1901 /*
1901 1902 * Found a 'title' line, parse out BE name or
1902 1903 * the description.
1903 1904 */
1904 1905 name = strtok(NULL, BE_WHITE_SPACE);
1905 1906
1906 1907 if (name == NULL) {
1907 1908 /*
1908 1909 * Nothing after 'title', just push
1909 1910 * this line through
1910 1911 */
1911 1912 (void) fputs(line, new_fp);
1912 1913 } else {
1913 1914 /*
1914 1915 * Grab the remainder of the title which
1915 1916 * could be a multi worded description
1916 1917 */
1917 1918 desc = strtok(NULL, "\n");
1918 1919
1919 1920 if (strcmp(name, be_orig_name) == 0) {
1920 1921 /*
1921 1922 * The first token of the title is
1922 1923 * the old BE name, replace it with
1923 1924 * the new one, and write it out
1924 1925 * along with the remainder of
1925 1926 * description if there is one.
1926 1927 */
1927 1928 if (desc) {
1928 1929 (void) snprintf(new_line,
1929 1930 sizeof (new_line),
1930 1931 "title %s %s\n",
1931 1932 be_new_name, desc);
1932 1933 } else {
1933 1934 (void) snprintf(new_line,
1934 1935 sizeof (new_line),
1935 1936 "title %s\n", be_new_name);
1936 1937 }
1937 1938
1938 1939 (void) fputs(new_line, new_fp);
1939 1940 } else {
1940 1941 (void) fputs(line, new_fp);
1941 1942 }
1942 1943 }
1943 1944 } else if (strcmp(c, "bootfs") == 0) {
1944 1945 /*
1945 1946 * Found a 'bootfs' line, parse out the BE root
1946 1947 * dataset value.
1947 1948 */
1948 1949 char *root_ds = strtok(NULL, BE_WHITE_SPACE);
1949 1950
1950 1951 if (root_ds == NULL) {
1951 1952 /*
1952 1953 * Nothing after 'bootfs', just push
1953 1954 * this line through
1954 1955 */
1955 1956 (void) fputs(line, new_fp);
1956 1957 } else {
1957 1958 /*
1958 1959 * If this bootfs is the one we're renaming,
1959 1960 * write out the new root dataset value
1960 1961 */
1961 1962 if (strcmp(root_ds, be_root_ds) == 0) {
1962 1963 (void) snprintf(new_line,
1963 1964 sizeof (new_line), "bootfs %s\n",
1964 1965 be_new_root_ds);
1965 1966
1966 1967 (void) fputs(new_line, new_fp);
1967 1968 } else {
1968 1969 (void) fputs(line, new_fp);
1969 1970 }
1970 1971 }
1971 1972 } else {
1972 1973 /*
1973 1974 * Found some other line we don't care
1974 1975 * about, write it out.
1975 1976 */
1976 1977 (void) fputs(line, new_fp);
1977 1978 }
1978 1979 }
1979 1980
1980 1981 (void) fclose(menu_fp);
1981 1982 (void) fclose(new_fp);
1982 1983 (void) close(tmp_fd);
1983 1984
1984 1985 if (rename(temp_menu, menu_file) != 0) {
1985 1986 err = errno;
1986 1987 be_print_err(gettext("be_update_menu: "
1987 1988 "failed to rename file %s to %s: %s\n"),
1988 1989 temp_menu, menu_file, strerror(err));
1989 1990 ret = errno_to_be_err(err);
1990 1991 }
1991 1992 free(temp_menu);
1992 1993
1993 1994 /* Set the perms and ownership of the updated file */
1994 1995 if (chmod(menu_file, sb.st_mode) != 0) {
1995 1996 err = errno;
1996 1997 be_print_err(gettext("be_update_menu: "
1997 1998 "failed to chmod %s: %s\n"), menu_file, strerror(err));
1998 1999 ret = errno_to_be_err(err);
1999 2000 goto cleanup;
2000 2001 }
2001 2002 if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) {
2002 2003 err = errno;
2003 2004 be_print_err(gettext("be_update_menu: "
2004 2005 "failed to chown %s: %s\n"), menu_file, strerror(err));
2005 2006 ret = errno_to_be_err(err);
2006 2007 }
2007 2008
2008 2009 cleanup:
2009 2010 if (pool_mounted) {
2010 2011 int err = BE_SUCCESS;
2011 2012 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
2012 2013 if (ret == BE_SUCCESS)
2013 2014 ret = err;
2014 2015 free(orig_mntpnt);
2015 2016 free(ptmp_mntpnt);
2016 2017 }
2017 2018 ZFS_CLOSE(zhp);
2018 2019 return (ret);
2019 2020 }
2020 2021
2021 2022 /*
2022 2023 * Function: be_has_menu_entry
2023 2024 * Description: Checks to see if the BEs root dataset has an entry in the grub
2024 2025 * menu.
2025 2026 * Parameters:
2026 2027 * be_dataset - The root dataset of the BE
2027 2028 * be_root_pool - The pool which contains the boot menu
2028 2029 * entry - A pointer the the entry number of the BE if found.
2029 2030 * Returns:
2030 2031 * B_TRUE - Success
2031 2032 * B_FALSE - Failure
2032 2033 * Scope:
2033 2034 * Semi-private (library wide use only)
2034 2035 */
2035 2036 boolean_t
2036 2037 be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry)
2037 2038 {
2038 2039 zfs_handle_t *zhp = NULL;
2039 2040 char menu_file[MAXPATHLEN];
2040 2041 FILE *menu_fp;
2041 2042 char line[BUFSIZ];
2042 2043 char *last;
2043 2044 char *rpool_mntpnt = NULL;
2044 2045 char *ptmp_mntpnt = NULL;
2045 2046 char *orig_mntpnt = NULL;
2046 2047 int ent_num = 0;
2047 2048 boolean_t ret = 0;
2048 2049 boolean_t pool_mounted = B_FALSE;
2049 2050
2050 2051
2051 2052 /*
2052 2053 * Check to see if this system supports grub
2053 2054 */
2054 2055 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
2055 2056 be_print_err(gettext("be_has_menu_entry: failed to open "
2056 2057 "pool dataset for %s: %s\n"), be_root_pool,
2057 2058 libzfs_error_description(g_zfs));
2058 2059 return (B_FALSE);
2059 2060 }
2060 2061
2061 2062 /*
2062 2063 * Check to see if the pool's dataset is mounted. If it isn't we'll
2063 2064 * attempt to mount it.
2064 2065 */
2065 2066 if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
2066 2067 &pool_mounted) != 0) {
2067 2068 be_print_err(gettext("be_has_menu_entry: pool dataset "
2068 2069 "(%s) could not be mounted\n"), be_root_pool);
2069 2070 ZFS_CLOSE(zhp);
2070 2071 return (B_FALSE);
2071 2072 }
2072 2073
2073 2074 /*
2074 2075 * Get the mountpoint for the root pool dataset.
2075 2076 */
2076 2077 if (!zfs_is_mounted(zhp, &rpool_mntpnt)) {
2077 2078 be_print_err(gettext("be_has_menu_entry: pool "
2078 2079 "dataset (%s) is not mounted. Can't set "
2079 2080 "the default BE in the grub menu.\n"), be_root_pool);
2080 2081 ret = B_FALSE;
2081 2082 goto cleanup;
2082 2083 }
2083 2084
2084 2085 if (be_has_grub()) {
2085 2086 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
2086 2087 rpool_mntpnt, BE_GRUB_MENU);
2087 2088 } else {
2088 2089 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
2089 2090 rpool_mntpnt, BE_SPARC_MENU);
2090 2091 }
2091 2092
2092 2093 if (be_open_menu(be_root_pool, menu_file, &menu_fp, "r",
2093 2094 B_FALSE) != 0) {
2094 2095 ret = B_FALSE;
2095 2096 goto cleanup;
2096 2097 } else if (menu_fp == NULL) {
2097 2098 ret = B_FALSE;
2098 2099 goto cleanup;
2099 2100 }
2100 2101
2101 2102 free(rpool_mntpnt);
2102 2103 rpool_mntpnt = NULL;
2103 2104
2104 2105 while (fgets(line, BUFSIZ, menu_fp)) {
2105 2106 char *tok = strtok_r(line, BE_WHITE_SPACE, &last);
2106 2107
2107 2108 if (tok != NULL && tok[0] != '#') {
2108 2109 if (strcmp(tok, "bootfs") == 0) {
2109 2110 tok = strtok_r(last, BE_WHITE_SPACE, &last);
2110 2111 if (tok != NULL && strcmp(tok,
2111 2112 be_dataset) == 0) {
2112 2113 (void) fclose(menu_fp);
2113 2114 /*
2114 2115 * The entry number needs to be
2115 2116 * decremented here because the title
2116 2117 * will always be the first line for
2117 2118 * an entry. Because of this we'll
2118 2119 * always be off by one entry when we
2119 2120 * check for bootfs.
2120 2121 */
2121 2122 *entry = ent_num - 1;
2122 2123 ret = B_TRUE;
2123 2124 goto cleanup;
2124 2125 }
2125 2126 } else if (strcmp(tok, "title") == 0)
2126 2127 ent_num++;
2127 2128 }
2128 2129 }
2129 2130
2130 2131 cleanup:
2131 2132 if (pool_mounted) {
2132 2133 (void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
2133 2134 free(orig_mntpnt);
2134 2135 free(ptmp_mntpnt);
2135 2136 }
2136 2137 ZFS_CLOSE(zhp);
2137 2138 (void) fclose(menu_fp);
2138 2139 return (ret);
2139 2140 }
2140 2141
2141 2142 /*
2142 2143 * Function: be_update_vfstab
2143 2144 * Description: This function digs into a BE's vfstab and updates all
2144 2145 * entries with file systems listed in be_fs_list_data_t.
2145 2146 * The entry's root container dataset and be_name will be
2146 2147 * updated with the parameters passed in.
2147 2148 * Parameters:
2148 2149 * be_name - name of BE to update
2149 2150 * old_rc_loc - dataset under which the root container dataset
2150 2151 * of the old BE resides in.
2151 2152 * new_rc_loc - dataset under which the root container dataset
2152 2153 * of the new BE resides in.
2153 2154 * fld - be_fs_list_data_t pointer providing the list of
2154 2155 * file systems to look for in vfstab.
2155 2156 * mountpoint - directory of where BE is currently mounted.
2156 2157 * If NULL, then BE is not currently mounted.
2157 2158 * Returns:
2158 2159 * BE_SUCCESS - Success
2159 2160 * be_errno_t - Failure
2160 2161 * Scope:
2161 2162 * Semi-private (library wide use only)
2162 2163 */
2163 2164 int
2164 2165 be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc,
2165 2166 be_fs_list_data_t *fld, char *mountpoint)
2166 2167 {
2167 2168 char *tmp_mountpoint = NULL;
2168 2169 char alt_vfstab[MAXPATHLEN];
2169 2170 int ret = BE_SUCCESS, err = BE_SUCCESS;
2170 2171
2171 2172 if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0)
2172 2173 return (BE_SUCCESS);
2173 2174
2174 2175 /* If BE not already mounted, mount the BE */
2175 2176 if (mountpoint == NULL) {
2176 2177 if ((ret = _be_mount(be_name, &tmp_mountpoint,
2177 2178 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
2178 2179 be_print_err(gettext("be_update_vfstab: "
2179 2180 "failed to mount BE (%s)\n"), be_name);
2180 2181 return (ret);
2181 2182 }
2182 2183 } else {
2183 2184 tmp_mountpoint = mountpoint;
2184 2185 }
2185 2186
2186 2187 /* Get string for vfstab in the mounted BE. */
2187 2188 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2188 2189 tmp_mountpoint);
2189 2190
2190 2191 /* Update the vfstab */
2191 2192 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2192 2193 fld);
2193 2194
2194 2195 /* Unmount BE if we mounted it */
2195 2196 if (mountpoint == NULL) {
2196 2197 if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
2197 2198 /* Remove temporary mountpoint */
2198 2199 (void) rmdir(tmp_mountpoint);
2199 2200 } else {
2200 2201 be_print_err(gettext("be_update_vfstab: "
2201 2202 "failed to unmount BE %s mounted at %s\n"),
2202 2203 be_name, tmp_mountpoint);
2203 2204 if (ret == BE_SUCCESS)
2204 2205 ret = err;
2205 2206 }
2206 2207
2207 2208 free(tmp_mountpoint);
2208 2209 }
2209 2210
2210 2211 return (ret);
2211 2212 }
2212 2213
2213 2214 /*
2214 2215 * Function: be_update_zone_vfstab
2215 2216 * Description: This function digs into a zone BE's vfstab and updates all
2216 2217 * entries with file systems listed in be_fs_list_data_t.
2217 2218 * The entry's root container dataset and be_name will be
2218 2219 * updated with the parameters passed in.
2219 2220 * Parameters:
2220 2221 * zhp - zfs_handle_t pointer to zone root dataset.
2221 2222 * be_name - name of zone BE to update
2222 2223 * old_rc_loc - dataset under which the root container dataset
2223 2224 * of the old zone BE resides in.
2224 2225 * new_rc_loc - dataset under which the root container dataset
2225 2226 * of the new zone BE resides in.
2226 2227 * fld - be_fs_list_data_t pointer providing the list of
2227 2228 * file systems to look for in vfstab.
2228 2229 * Returns:
2229 2230 * BE_SUCCESS - Success
2230 2231 * be_errno_t - Failure
2231 2232 * Scope:
2232 2233 * Semi-private (library wide use only)
2233 2234 */
2234 2235 int
2235 2236 be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc,
2236 2237 char *new_rc_loc, be_fs_list_data_t *fld)
2237 2238 {
2238 2239 be_mount_data_t md = { 0 };
2239 2240 be_unmount_data_t ud = { 0 };
2240 2241 char alt_vfstab[MAXPATHLEN];
2241 2242 boolean_t mounted_here = B_FALSE;
2242 2243 int ret = BE_SUCCESS;
2243 2244
2244 2245 /*
2245 2246 * If zone root not already mounted, mount it at a
2246 2247 * temporary location.
2247 2248 */
2248 2249 if (!zfs_is_mounted(zhp, &md.altroot)) {
2249 2250 /* Generate temporary mountpoint to mount zone root */
2250 2251 if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) {
2251 2252 be_print_err(gettext("be_update_zone_vfstab: "
2252 2253 "failed to make temporary mountpoint to "
2253 2254 "mount zone root\n"));
2254 2255 return (ret);
2255 2256 }
2256 2257
2257 2258 if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) {
2258 2259 be_print_err(gettext("be_update_zone_vfstab: "
2259 2260 "failed to mount zone root %s\n"),
2260 2261 zfs_get_name(zhp));
2261 2262 free(md.altroot);
2262 2263 return (BE_ERR_MOUNT_ZONEROOT);
2263 2264 }
2264 2265 mounted_here = B_TRUE;
2265 2266 }
2266 2267
2267 2268 /* Get string from vfstab in the mounted zone BE */
2268 2269 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2269 2270 md.altroot);
2270 2271
2271 2272 /* Update the vfstab */
2272 2273 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2273 2274 fld);
2274 2275
2275 2276 /* Unmount zone root if we mounted it */
2276 2277 if (mounted_here) {
2277 2278 ud.force = B_TRUE;
2278 2279
2279 2280 if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
2280 2281 /* Remove the temporary mountpoint */
2281 2282 (void) rmdir(md.altroot);
2282 2283 } else {
2283 2284 be_print_err(gettext("be_update_zone_vfstab: "
2284 2285 "failed to unmount zone root %s from %s\n"),
2285 2286 zfs_get_name(zhp), md.altroot);
2286 2287 if (ret == 0)
2287 2288 ret = BE_ERR_UMOUNT_ZONEROOT;
2288 2289 }
2289 2290 }
2290 2291
2291 2292 free(md.altroot);
2292 2293 return (ret);
2293 2294 }
2294 2295
2295 2296 /*
2296 2297 * Function: be_auto_snap_name
2297 2298 * Description: Generate an auto snapshot name constructed based on the
2298 2299 * current date and time. The auto snapshot name is of the form:
2299 2300 *
2300 2301 * <date>-<time>
2301 2302 *
2302 2303 * where <date> is in ISO standard format, so the resultant name
2303 2304 * is of the form:
2304 2305 *
2305 2306 * %Y-%m-%d-%H:%M:%S
2306 2307 *
2307 2308 * Parameters:
2308 2309 * None
2309 2310 * Returns:
2310 2311 * Success - pointer to auto generated snapshot name. The name
2311 2312 * is allocated in heap storage so the caller is
2312 2313 * responsible for free'ing the name.
2313 2314 * Failure - NULL
2314 2315 * Scope:
2315 2316 * Semi-private (library wide use only)
2316 2317 */
2317 2318 char *
2318 2319 be_auto_snap_name(void)
2319 2320 {
2320 2321 time_t utc_tm = NULL;
2321 2322 struct tm *gmt_tm = NULL;
2322 2323 char gmt_time_str[64];
2323 2324 char *auto_snap_name = NULL;
2324 2325
2325 2326 if (time(&utc_tm) == -1) {
2326 2327 be_print_err(gettext("be_auto_snap_name: time() failed\n"));
2327 2328 return (NULL);
2328 2329 }
2329 2330
2330 2331 if ((gmt_tm = gmtime(&utc_tm)) == NULL) {
2331 2332 be_print_err(gettext("be_auto_snap_name: gmtime() failed\n"));
2332 2333 return (NULL);
2333 2334 }
2334 2335
2335 2336 (void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm);
2336 2337
2337 2338 if ((auto_snap_name = strdup(gmt_time_str)) == NULL) {
2338 2339 be_print_err(gettext("be_auto_snap_name: "
2339 2340 "memory allocation failed\n"));
2340 2341 return (NULL);
2341 2342 }
2342 2343
2343 2344 return (auto_snap_name);
2344 2345 }
2345 2346
2346 2347 /*
2347 2348 * Function: be_auto_be_name
2348 2349 * Description: Generate an auto BE name constructed based on the BE name
2349 2350 * of the original BE being cloned.
2350 2351 * Parameters:
2351 2352 * obe_name - name of the original BE being cloned.
2352 2353 * Returns:
2353 2354 * Success - pointer to auto generated BE name. The name
2354 2355 * is allocated in heap storage so the caller is
2355 2356 * responsible for free'ing the name.
2356 2357 * Failure - NULL
2357 2358 * Scope:
2358 2359 * Semi-private (library wide use only)
2359 2360 */
2360 2361 char *
2361 2362 be_auto_be_name(char *obe_name)
2362 2363 {
2363 2364 return (be_get_auto_name(obe_name, NULL, B_FALSE));
2364 2365 }
2365 2366
2366 2367 /*
2367 2368 * Function: be_auto_zone_be_name
2368 2369 * Description: Generate an auto BE name for a zone constructed based on
2369 2370 * the BE name of the original zone BE being cloned.
2370 2371 * Parameters:
2371 2372 * container_ds - container dataset for the zone.
2372 2373 * zbe_name - name of the original zone BE being cloned.
2373 2374 * Returns:
2374 2375 * Success - pointer to auto generated BE name. The name
2375 2376 * is allocated in heap storage so the caller is
2376 2377 * responsible for free'ing the name.
2377 2378 * Failure - NULL
2378 2379 * Scope:
2379 2380 * Semi-private (library wide use only)
2380 2381 */
2381 2382 char *
2382 2383 be_auto_zone_be_name(char *container_ds, char *zbe_name)
2383 2384 {
2384 2385 return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
2385 2386 }
2386 2387
2387 2388 /*
2388 2389 * Function: be_valid_be_name
2389 2390 * Description: Validates a BE name.
2390 2391 * Parameters:
2391 2392 * be_name - name of BE to validate
2392 2393 * Returns:
2393 2394 * B_TRUE - be_name is valid
2394 2395 * B_FALSE - be_name is invalid
2395 2396 * Scope:
2396 2397 * Semi-private (library wide use only)
2397 2398 */
2398 2399
2399 2400 boolean_t
2400 2401 be_valid_be_name(const char *be_name)
2401 2402 {
2402 2403 const char *c = NULL;
2403 2404 struct be_defaults be_defaults;
2404 2405
2405 2406 if (be_name == NULL)
2406 2407 return (B_FALSE);
2407 2408
2408 2409 be_get_defaults(&be_defaults);
2409 2410
2410 2411 /*
2411 2412 * A BE name must not be a multi-level dataset name. We also check
2412 2413 * that it does not contain the ' ' and '%' characters. The ' ' is
2413 2414 * a valid character for datasets, however we don't allow that in a
2414 2415 * BE name. The '%' is invalid, but zfs_name_valid() allows it for
2415 2416 * internal reasons, so we explicitly check for it here.
2416 2417 */
2417 2418 c = be_name;
2418 2419 while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
2419 2420 c++;
2420 2421
2421 2422 if (*c != '\0')
2422 2423 return (B_FALSE);
2423 2424
2424 2425 /*
2425 2426 * The BE name must comply with a zfs dataset filesystem. We also
2426 2427 * verify its length to be < BE_NAME_MAX_LEN.
2427 2428 */
2428 2429 if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
2429 2430 strlen(be_name) > BE_NAME_MAX_LEN)
2430 2431 return (B_FALSE);
2431 2432
2432 2433 if (be_defaults.be_deflt_bename_starts_with[0] != '\0' &&
2433 2434 strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) {
2434 2435 return (B_FALSE);
2435 2436 }
2436 2437
2437 2438 return (B_TRUE);
2438 2439 }
2439 2440
2440 2441 /*
2441 2442 * Function: be_valid_auto_snap_name
2442 2443 * Description: This function checks that a snapshot name is a valid auto
2443 2444 * generated snapshot name. A valid auto generated snapshot
2444 2445 * name is of the form:
2445 2446 *
2446 2447 * %Y-%m-%d-%H:%M:%S
2447 2448 *
2448 2449 * An older form of the auto generated snapshot name also
2449 2450 * included the snapshot's BE cleanup policy and a reserved
2450 2451 * field. Those names will also be verified by this function.
2451 2452 *
2452 2453 * Examples of valid auto snapshot names are:
2453 2454 *
2454 2455 * 2008-03-31-18:41:30
2455 2456 * 2008-03-31-22:17:24
2456 2457 * <policy>:-:2008:04-05-09:12:55
2457 2458 * <policy>:-:2008:04-06-15:34:12
2458 2459 *
2459 2460 * Parameters:
2460 2461 * name - name of the snapshot to be validated.
2461 2462 * Returns:
2462 2463 * B_TRUE - the name is a valid auto snapshot name.
2463 2464 * B_FALSE - the name is not a valid auto snapshot name.
2464 2465 * Scope:
2465 2466 * Semi-private (library wide use only)
2466 2467 */
2467 2468 boolean_t
2468 2469 be_valid_auto_snap_name(char *name)
2469 2470 {
2470 2471 struct tm gmt_tm;
2471 2472
2472 2473 char *policy = NULL;
2473 2474 char *reserved = NULL;
2474 2475 char *date = NULL;
2475 2476 char *c = NULL;
2476 2477
2477 2478 /* Validate the snapshot name by converting it into utc time */
2478 2479 if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
2479 2480 (mktime(&gmt_tm) != -1)) {
2480 2481 return (B_TRUE);
2481 2482 }
2482 2483
2483 2484 /*
2484 2485 * Validate the snapshot name against the older form of an
2485 2486 * auto generated snapshot name.
2486 2487 */
2487 2488 policy = strdup(name);
2488 2489
2489 2490 /*
2490 2491 * Get the first field from the snapshot name,
2491 2492 * which is the BE policy
2492 2493 */
2493 2494 c = strchr(policy, ':');
2494 2495 if (c == NULL) {
2495 2496 free(policy);
2496 2497 return (B_FALSE);
2497 2498 }
2498 2499 c[0] = '\0';
2499 2500
2500 2501 /* Validate the policy name */
2501 2502 if (!valid_be_policy(policy)) {
2502 2503 free(policy);
2503 2504 return (B_FALSE);
2504 2505 }
2505 2506
2506 2507 /* Get the next field, which is the reserved field. */
2507 2508 if (c[1] == NULL || c[1] == '\0') {
2508 2509 free(policy);
2509 2510 return (B_FALSE);
2510 2511 }
2511 2512 reserved = c+1;
2512 2513 c = strchr(reserved, ':');
2513 2514 if (c == NULL) {
2514 2515 free(policy);
2515 2516 return (B_FALSE);
2516 2517 }
2517 2518 c[0] = '\0';
2518 2519
2519 2520 /* Validate the reserved field */
2520 2521 if (strcmp(reserved, "-") != 0) {
2521 2522 free(policy);
2522 2523 return (B_FALSE);
2523 2524 }
2524 2525
2525 2526 /* The remaining string should be the date field */
2526 2527 if (c[1] == NULL || c[1] == '\0') {
2527 2528 free(policy);
2528 2529 return (B_FALSE);
2529 2530 }
2530 2531 date = c+1;
2531 2532
2532 2533 /* Validate the date string by converting it into utc time */
2533 2534 if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL ||
2534 2535 (mktime(&gmt_tm) == -1)) {
2535 2536 be_print_err(gettext("be_valid_auto_snap_name: "
2536 2537 "invalid auto snapshot name\n"));
2537 2538 free(policy);
2538 2539 return (B_FALSE);
2539 2540 }
2540 2541
2541 2542 free(policy);
2542 2543 return (B_TRUE);
2543 2544 }
2544 2545
2545 2546 /*
2546 2547 * Function: be_default_policy
2547 2548 * Description: Temporary hardcoded policy support. This function returns
2548 2549 * the default policy type to be used to create a BE or a BE
2549 2550 * snapshot.
2550 2551 * Parameters:
2551 2552 * None
2552 2553 * Returns:
2553 2554 * Name of default BE policy.
2554 2555 * Scope:
2555 2556 * Semi-private (library wide use only)
2556 2557 */
2557 2558 char *
2558 2559 be_default_policy(void)
2559 2560 {
2560 2561 return (BE_PLCY_STATIC);
2561 2562 }
2562 2563
2563 2564 /*
2564 2565 * Function: valid_be_policy
2565 2566 * Description: Temporary hardcoded policy support. This function valids
2566 2567 * whether a policy is a valid known policy or not.
2567 2568 * Paramters:
2568 2569 * policy - name of policy to validate.
2569 2570 * Returns:
2570 2571 * B_TRUE - policy is a valid.
2571 2572 * B_FALSE - policy is invalid.
2572 2573 * Scope:
2573 2574 * Semi-private (library wide use only)
2574 2575 */
2575 2576 boolean_t
2576 2577 valid_be_policy(char *policy)
2577 2578 {
2578 2579 if (policy == NULL)
2579 2580 return (B_FALSE);
2580 2581
2581 2582 if (strcmp(policy, BE_PLCY_STATIC) == 0 ||
2582 2583 strcmp(policy, BE_PLCY_VOLATILE) == 0) {
2583 2584 return (B_TRUE);
2584 2585 }
2585 2586
2586 2587 return (B_FALSE);
2587 2588 }
2588 2589
2589 2590 /*
2590 2591 * Function: be_print_err
2591 2592 * Description: This function prints out error messages if do_print is
2592 2593 * set to B_TRUE or if the BE_PRINT_ERR environment variable
2593 2594 * is set to true.
2594 2595 * Paramters:
2595 2596 * prnt_str - the string we wish to print and any arguments
2596 2597 * for the format of that string.
2597 2598 * Returns:
2598 2599 * void
2599 2600 * Scope:
2600 2601 * Semi-private (library wide use only)
2601 2602 */
2602 2603 void
2603 2604 be_print_err(char *prnt_str, ...)
2604 2605 {
2605 2606 va_list ap;
2606 2607 char buf[BUFSIZ];
2607 2608 char *env_buf;
2608 2609 static boolean_t env_checked = B_FALSE;
2609 2610
2610 2611 if (!env_checked) {
2611 2612 if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) {
2612 2613 if (strcasecmp(env_buf, "true") == 0) {
2613 2614 do_print = B_TRUE;
2614 2615 }
2615 2616 }
2616 2617 env_checked = B_TRUE;
2617 2618 }
2618 2619
2619 2620 if (do_print) {
2620 2621 va_start(ap, prnt_str);
2621 2622 /* LINTED variable format specifier */
2622 2623 (void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
2623 2624 (void) fputs(buf, stderr);
2624 2625 va_end(ap);
2625 2626 }
2626 2627 }
2627 2628
2628 2629 /*
2629 2630 * Function: be_find_current_be
2630 2631 * Description: Find the currently "active" BE. Fill in the
2631 2632 * passed in be_transaction_data_t reference with the
2632 2633 * active BE's data.
2633 2634 * Paramters:
2634 2635 * none
2635 2636 * Returns:
2636 2637 * BE_SUCCESS - Success
2637 2638 * be_errnot_t - Failure
2638 2639 * Scope:
2639 2640 * Semi-private (library wide use only)
2640 2641 * Notes:
2641 2642 * The caller is responsible for initializing the libzfs handle
2642 2643 * and freeing the memory used by the active be_name.
2643 2644 */
2644 2645 int
2645 2646 be_find_current_be(be_transaction_data_t *bt)
2646 2647 {
2647 2648 int zret;
2648 2649
2649 2650 if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback,
2650 2651 bt)) == 0) {
2651 2652 be_print_err(gettext("be_find_current_be: failed to "
2652 2653 "find current BE name\n"));
2653 2654 return (BE_ERR_BE_NOENT);
2654 2655 } else if (zret < 0) {
2655 2656 be_print_err(gettext("be_find_current_be: "
2656 2657 "zpool_iter failed: %s\n"),
2657 2658 libzfs_error_description(g_zfs));
2658 2659 return (zfs_err_to_be_err(g_zfs));
2659 2660 }
2660 2661
2661 2662 return (BE_SUCCESS);
2662 2663 }
2663 2664
2664 2665 /*
2665 2666 * Function: be_zpool_find_current_be_callback
2666 2667 * Description: Callback function used to iterate through all existing pools
2667 2668 * to find the BE that is the currently booted BE.
2668 2669 * Parameters:
2669 2670 * zlp - zpool_handle_t pointer to the current pool being
2670 2671 * looked at.
2671 2672 * data - be_transaction_data_t pointer.
2672 2673 * Upon successfully finding the current BE, the
2673 2674 * obe_zpool member of this parameter is set to the
2674 2675 * pool it is found in.
2675 2676 * Return:
2676 2677 * 1 - Found current BE in this pool.
2677 2678 * 0 - Did not find current BE in this pool.
2678 2679 * Scope:
2679 2680 * Semi-private (library wide use only)
2680 2681 */
2681 2682 int
2682 2683 be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data)
2683 2684 {
2684 2685 be_transaction_data_t *bt = data;
2685 2686 zfs_handle_t *zhp = NULL;
2686 2687 const char *zpool = zpool_get_name(zlp);
2687 2688 char be_container_ds[MAXPATHLEN];
2688 2689 char *zpath = NULL;
2689 2690
2690 2691 /*
2691 2692 * Generate string for BE container dataset
2692 2693 */
2693 2694 if (getzoneid() != GLOBAL_ZONEID) {
2694 2695 if ((zpath = be_get_ds_from_dir("/")) != NULL) {
2695 2696 (void) strlcpy(be_container_ds, dirname(zpath),
2696 2697 sizeof (be_container_ds));
2697 2698 } else {
2698 2699 be_print_err(gettext(
2699 2700 "be_zpool_find_current_be_callback: "
2700 2701 "zone root dataset is not mounted\n"));
2701 2702 return (0);
2702 2703 }
2703 2704 } else {
2704 2705 be_make_container_ds(zpool, be_container_ds,
2705 2706 sizeof (be_container_ds));
2706 2707 }
2707 2708
2708 2709 /*
2709 2710 * Check if a BE container dataset exists in this pool.
2710 2711 */
2711 2712 if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
2712 2713 zpool_close(zlp);
2713 2714 return (0);
2714 2715 }
2715 2716
2716 2717 /*
2717 2718 * Get handle to this zpool's BE container dataset.
2718 2719 */
2719 2720 if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) ==
2720 2721 NULL) {
2721 2722 be_print_err(gettext("be_zpool_find_current_be_callback: "
2722 2723 "failed to open BE container dataset (%s)\n"),
2723 2724 be_container_ds);
2724 2725 zpool_close(zlp);
2725 2726 return (0);
2726 2727 }
2727 2728
2728 2729 /*
2729 2730 * Iterate through all potential BEs in this zpool
2730 2731 */
2731 2732 if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
2732 2733 /*
2733 2734 * Found current BE dataset; set obe_zpool
2734 2735 */
2735 2736 if ((bt->obe_zpool = strdup(zpool)) == NULL) {
2736 2737 be_print_err(gettext(
2737 2738 "be_zpool_find_current_be_callback: "
2738 2739 "memory allocation failed\n"));
2739 2740 ZFS_CLOSE(zhp);
2740 2741 zpool_close(zlp);
2741 2742 return (0);
2742 2743 }
2743 2744
2744 2745 ZFS_CLOSE(zhp);
2745 2746 zpool_close(zlp);
2746 2747 return (1);
2747 2748 }
2748 2749
2749 2750 ZFS_CLOSE(zhp);
2750 2751 zpool_close(zlp);
2751 2752
2752 2753 return (0);
2753 2754 }
2754 2755
2755 2756 /*
2756 2757 * Function: be_zfs_find_current_be_callback
2757 2758 * Description: Callback function used to iterate through all BEs in a
2758 2759 * pool to find the BE that is the currently booted BE.
2759 2760 * Parameters:
2760 2761 * zhp - zfs_handle_t pointer to current filesystem being checked.
2761 2762 * data - be_transaction-data_t pointer
2762 2763 * Upon successfully finding the current BE, the
2763 2764 * obe_name and obe_root_ds members of this parameter
2764 2765 * are set to the BE name and BE's root dataset
2765 2766 * respectively.
2766 2767 * Return:
2767 2768 * 1 - Found current BE.
2768 2769 * 0 - Did not find current BE.
2769 2770 * Scope:
2770 2771 * Semi-private (library wide use only)
2771 2772 */
2772 2773 int
2773 2774 be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
2774 2775 {
2775 2776 be_transaction_data_t *bt = data;
2776 2777 char *mp = NULL;
2777 2778
2778 2779 /*
2779 2780 * Check if dataset is mounted, and if so where.
2780 2781 */
2781 2782 if (zfs_is_mounted(zhp, &mp)) {
2782 2783 /*
2783 2784 * If mounted at root, set obe_root_ds and obe_name
2784 2785 */
2785 2786 if (mp != NULL && strcmp(mp, "/") == 0) {
2786 2787 free(mp);
2787 2788
2788 2789 if ((bt->obe_root_ds = strdup(zfs_get_name(zhp)))
2789 2790 == NULL) {
2790 2791 be_print_err(gettext(
2791 2792 "be_zfs_find_current_be_callback: "
2792 2793 "memory allocation failed\n"));
2793 2794 ZFS_CLOSE(zhp);
2794 2795 return (0);
2795 2796 }
2796 2797
2797 2798 if ((bt->obe_name = strdup(basename(bt->obe_root_ds)))
2798 2799 == NULL) {
2799 2800 be_print_err(gettext(
2800 2801 "be_zfs_find_current_be_callback: "
2801 2802 "memory allocation failed\n"));
2802 2803 ZFS_CLOSE(zhp);
2803 2804 return (0);
2804 2805 }
2805 2806
2806 2807 ZFS_CLOSE(zhp);
2807 2808 return (1);
2808 2809 }
2809 2810
2810 2811 free(mp);
2811 2812 }
2812 2813 ZFS_CLOSE(zhp);
2813 2814
2814 2815 return (0);
2815 2816 }
2816 2817
2817 2818 /*
2818 2819 * Function: be_check_be_roots_callback
2819 2820 * Description: This function checks whether or not the dataset name passed
2820 2821 * is hierachically located under the BE root container dataset
2821 2822 * for this pool.
2822 2823 * Parameters:
2823 2824 * zlp - zpool_handle_t pointer to current pool being processed.
2824 2825 * data - name of dataset to check
2825 2826 * Returns:
2826 2827 * 0 - dataset is not in this pool's BE root container dataset
2827 2828 * 1 - dataset is in this pool's BE root container dataset
2828 2829 * Scope:
2829 2830 * Semi-private (library wide use only)
2830 2831 */
2831 2832 int
2832 2833 be_check_be_roots_callback(zpool_handle_t *zlp, void *data)
2833 2834 {
2834 2835 const char *zpool = zpool_get_name(zlp);
2835 2836 char *ds = data;
2836 2837 char be_container_ds[MAXPATHLEN];
2837 2838
2838 2839 /* Generate string for this pool's BE root container dataset */
2839 2840 be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
2840 2841
2841 2842 /*
2842 2843 * If dataset lives under the BE root container dataset
2843 2844 * of this pool, return failure.
2844 2845 */
2845 2846 if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 &&
2846 2847 ds[strlen(be_container_ds)] == '/') {
2847 2848 zpool_close(zlp);
2848 2849 return (1);
2849 2850 }
2850 2851
2851 2852 zpool_close(zlp);
2852 2853 return (0);
2853 2854 }
2854 2855
2855 2856 /*
2856 2857 * Function: zfs_err_to_be_err
2857 2858 * Description: This function takes the error stored in the libzfs handle
2858 2859 * and maps it to an be_errno_t. If there are no matching
2859 2860 * be_errno_t's then BE_ERR_ZFS is returned.
2860 2861 * Paramters:
2861 2862 * zfsh - The libzfs handle containing the error we're looking up.
2862 2863 * Returns:
2863 2864 * be_errno_t
2864 2865 * Scope:
2865 2866 * Semi-private (library wide use only)
2866 2867 */
2867 2868 int
2868 2869 zfs_err_to_be_err(libzfs_handle_t *zfsh)
2869 2870 {
2870 2871 int err = libzfs_errno(zfsh);
2871 2872
2872 2873 switch (err) {
2873 2874 case 0:
2874 2875 return (BE_SUCCESS);
2875 2876 case EZFS_PERM:
2876 2877 return (BE_ERR_PERM);
2877 2878 case EZFS_INTR:
2878 2879 return (BE_ERR_INTR);
2879 2880 case EZFS_NOENT:
2880 2881 return (BE_ERR_NOENT);
2881 2882 case EZFS_NOSPC:
2882 2883 return (BE_ERR_NOSPC);
2883 2884 case EZFS_MOUNTFAILED:
2884 2885 return (BE_ERR_MOUNT);
2885 2886 case EZFS_UMOUNTFAILED:
2886 2887 return (BE_ERR_UMOUNT);
2887 2888 case EZFS_EXISTS:
2888 2889 return (BE_ERR_BE_EXISTS);
2889 2890 case EZFS_BUSY:
2890 2891 return (BE_ERR_DEV_BUSY);
2891 2892 case EZFS_POOLREADONLY:
2892 2893 return (BE_ERR_ROFS);
2893 2894 case EZFS_NAMETOOLONG:
2894 2895 return (BE_ERR_NAMETOOLONG);
2895 2896 case EZFS_NODEVICE:
2896 2897 return (BE_ERR_NODEV);
2897 2898 case EZFS_POOL_INVALARG:
2898 2899 return (BE_ERR_INVAL);
2899 2900 case EZFS_PROPTYPE:
2900 2901 return (BE_ERR_INVALPROP);
2901 2902 case EZFS_BADTYPE:
2902 2903 return (BE_ERR_DSTYPE);
2903 2904 case EZFS_PROPNONINHERIT:
2904 2905 return (BE_ERR_NONINHERIT);
2905 2906 case EZFS_PROPREADONLY:
2906 2907 return (BE_ERR_READONLYPROP);
2907 2908 case EZFS_RESILVERING:
2908 2909 case EZFS_POOLUNAVAIL:
2909 2910 return (BE_ERR_UNAVAIL);
2910 2911 case EZFS_DSREADONLY:
2911 2912 return (BE_ERR_READONLYDS);
2912 2913 default:
2913 2914 return (BE_ERR_ZFS);
2914 2915 }
2915 2916 }
2916 2917
2917 2918 /*
2918 2919 * Function: errno_to_be_err
2919 2920 * Description: This function takes an errno and maps it to an be_errno_t.
2920 2921 * If there are no matching be_errno_t's then BE_ERR_UNKNOWN is
2921 2922 * returned.
2922 2923 * Paramters:
2923 2924 * err - The errno we're compairing against.
2924 2925 * Returns:
2925 2926 * be_errno_t
2926 2927 * Scope:
2927 2928 * Semi-private (library wide use only)
2928 2929 */
2929 2930 int
2930 2931 errno_to_be_err(int err)
2931 2932 {
2932 2933 switch (err) {
2933 2934 case EPERM:
2934 2935 return (BE_ERR_PERM);
2935 2936 case EACCES:
2936 2937 return (BE_ERR_ACCESS);
2937 2938 case ECANCELED:
2938 2939 return (BE_ERR_CANCELED);
2939 2940 case EINTR:
2940 2941 return (BE_ERR_INTR);
2941 2942 case ENOENT:
2942 2943 return (BE_ERR_NOENT);
2943 2944 case ENOSPC:
2944 2945 case EDQUOT:
2945 2946 return (BE_ERR_NOSPC);
2946 2947 case EEXIST:
2947 2948 return (BE_ERR_BE_EXISTS);
2948 2949 case EBUSY:
2949 2950 return (BE_ERR_BUSY);
2950 2951 case EROFS:
2951 2952 return (BE_ERR_ROFS);
2952 2953 case ENAMETOOLONG:
2953 2954 return (BE_ERR_NAMETOOLONG);
2954 2955 case ENXIO:
2955 2956 return (BE_ERR_NXIO);
2956 2957 case EINVAL:
2957 2958 return (BE_ERR_INVAL);
2958 2959 case EFAULT:
2959 2960 return (BE_ERR_FAULT);
2960 2961 default:
2961 2962 return (BE_ERR_UNKNOWN);
2962 2963 }
2963 2964 }
2964 2965
2965 2966 /*
2966 2967 * Function: be_err_to_str
2967 2968 * Description: This function takes a be_errno_t and maps it to a message.
2968 2969 * If there are no matching be_errno_t's then NULL is returned.
2969 2970 * Paramters:
2970 2971 * be_errno_t - The be_errno_t we're mapping.
2971 2972 * Returns:
2972 2973 * string or NULL if the error code is not known.
2973 2974 * Scope:
2974 2975 * Semi-private (library wide use only)
2975 2976 */
2976 2977 char *
2977 2978 be_err_to_str(int err)
2978 2979 {
2979 2980 switch (err) {
2980 2981 case BE_ERR_ACCESS:
2981 2982 return (gettext("Permission denied."));
2982 2983 case BE_ERR_ACTIVATE_CURR:
2983 2984 return (gettext("Activation of current BE failed."));
2984 2985 case BE_ERR_AUTONAME:
2985 2986 return (gettext("Auto naming failed."));
2986 2987 case BE_ERR_BE_NOENT:
2987 2988 return (gettext("No such BE."));
2988 2989 case BE_ERR_BUSY:
2989 2990 return (gettext("Mount busy."));
2990 2991 case BE_ERR_DEV_BUSY:
2991 2992 return (gettext("Device busy."));
2992 2993 case BE_ERR_CANCELED:
2993 2994 return (gettext("Operation canceled."));
2994 2995 case BE_ERR_CLONE:
2995 2996 return (gettext("BE clone failed."));
2996 2997 case BE_ERR_COPY:
2997 2998 return (gettext("BE copy failed."));
2998 2999 case BE_ERR_CREATDS:
2999 3000 return (gettext("Dataset creation failed."));
3000 3001 case BE_ERR_CURR_BE_NOT_FOUND:
3001 3002 return (gettext("Can't find current BE."));
3002 3003 case BE_ERR_DESTROY:
3003 3004 return (gettext("Failed to destroy BE or snapshot."));
3004 3005 case BE_ERR_DESTROY_CURR_BE:
3005 3006 return (gettext("Cannot destroy current BE."));
3006 3007 case BE_ERR_DEMOTE:
3007 3008 return (gettext("BE demotion failed."));
3008 3009 case BE_ERR_DSTYPE:
3009 3010 return (gettext("Invalid dataset type."));
3010 3011 case BE_ERR_BE_EXISTS:
3011 3012 return (gettext("BE exists."));
3012 3013 case BE_ERR_INIT:
3013 3014 return (gettext("be_zfs_init failed."));
3014 3015 case BE_ERR_INTR:
3015 3016 return (gettext("Interupted system call."));
3016 3017 case BE_ERR_INVAL:
3017 3018 return (gettext("Invalid argument."));
3018 3019 case BE_ERR_INVALPROP:
3019 3020 return (gettext("Invalid property for dataset."));
3020 3021 case BE_ERR_INVALMOUNTPOINT:
3021 3022 return (gettext("Unexpected mountpoint."));
3022 3023 case BE_ERR_MOUNT:
3023 3024 return (gettext("Mount failed."));
3024 3025 case BE_ERR_MOUNTED:
3025 3026 return (gettext("Already mounted."));
3026 3027 case BE_ERR_NAMETOOLONG:
3027 3028 return (gettext("name > BUFSIZ."));
3028 3029 case BE_ERR_NOENT:
3029 3030 return (gettext("Doesn't exist."));
3030 3031 case BE_ERR_POOL_NOENT:
3031 3032 return (gettext("No such pool."));
3032 3033 case BE_ERR_NODEV:
3033 3034 return (gettext("No such device."));
3034 3035 case BE_ERR_NOTMOUNTED:
3035 3036 return (gettext("File system not mounted."));
3036 3037 case BE_ERR_NOMEM:
3037 3038 return (gettext("Not enough memory."));
3038 3039 case BE_ERR_NONINHERIT:
3039 3040 return (gettext(
3040 3041 "Property is not inheritable for the BE dataset."));
3041 3042 case BE_ERR_NXIO:
3042 3043 return (gettext("No such device or address."));
3043 3044 case BE_ERR_NOSPC:
3044 3045 return (gettext("No space on device."));
3045 3046 case BE_ERR_NOTSUP:
3046 3047 return (gettext("Operation not supported."));
3047 3048 case BE_ERR_OPEN:
3048 3049 return (gettext("Open failed."));
3049 3050 case BE_ERR_PERM:
3050 3051 return (gettext("Not owner."));
3051 3052 case BE_ERR_UNAVAIL:
3052 3053 return (gettext("The BE is currently unavailable."));
3053 3054 case BE_ERR_PROMOTE:
3054 3055 return (gettext("BE promotion failed."));
3055 3056 case BE_ERR_ROFS:
3056 3057 return (gettext("Read only file system."));
3057 3058 case BE_ERR_READONLYDS:
3058 3059 return (gettext("Read only dataset."));
3059 3060 case BE_ERR_READONLYPROP:
3060 3061 return (gettext("Read only property."));
3061 3062 case BE_ERR_RENAME_ACTIVE:
3062 3063 return (gettext("Renaming the active BE is not supported."));
3063 3064 case BE_ERR_SS_EXISTS:
3064 3065 return (gettext("Snapshot exists."));
3065 3066 case BE_ERR_SS_NOENT:
3066 3067 return (gettext("No such snapshot."));
3067 3068 case BE_ERR_UMOUNT:
3068 3069 return (gettext("Unmount failed."));
3069 3070 case BE_ERR_UMOUNT_CURR_BE:
3070 3071 return (gettext("Can't unmount the current BE."));
3071 3072 case BE_ERR_UMOUNT_SHARED:
3072 3073 return (gettext("Unmount of a shared File System failed."));
3073 3074 case BE_ERR_FAULT:
3074 3075 return (gettext("Bad address."));
3075 3076 case BE_ERR_UNKNOWN:
3076 3077 return (gettext("Unknown error."));
3077 3078 case BE_ERR_ZFS:
3078 3079 return (gettext("ZFS returned an error."));
3079 3080 case BE_ERR_GEN_UUID:
3080 3081 return (gettext("Failed to generate uuid."));
3081 3082 case BE_ERR_PARSE_UUID:
3082 3083 return (gettext("Failed to parse uuid."));
3083 3084 case BE_ERR_NO_UUID:
3084 3085 return (gettext("No uuid"));
3085 3086 case BE_ERR_ZONE_NO_PARENTBE:
3086 3087 return (gettext("No parent uuid"));
3087 3088 case BE_ERR_ZONE_MULTIPLE_ACTIVE:
3088 3089 return (gettext("Multiple active zone roots"));
3089 3090 case BE_ERR_ZONE_NO_ACTIVE_ROOT:
3090 3091 return (gettext("No active zone root"));
3091 3092 case BE_ERR_ZONE_ROOT_NOT_LEGACY:
3092 3093 return (gettext("Zone root not legacy"));
3093 3094 case BE_ERR_MOUNT_ZONEROOT:
3094 3095 return (gettext("Failed to mount a zone root."));
3095 3096 case BE_ERR_UMOUNT_ZONEROOT:
3096 3097 return (gettext("Failed to unmount a zone root."));
3097 3098 case BE_ERR_NO_MOUNTED_ZONE:
3098 3099 return (gettext("Zone is not mounted"));
3099 3100 case BE_ERR_ZONES_UNMOUNT:
3100 3101 return (gettext("Unable to unmount a zone BE."));
3101 3102 case BE_ERR_NO_MENU:
3102 3103 return (gettext("Missing boot menu file."));
3103 3104 case BE_ERR_BAD_MENU_PATH:
3104 3105 return (gettext("Invalid path for menu.lst file"));
3105 3106 case BE_ERR_ZONE_SS_EXISTS:
3106 3107 return (gettext("Zone snapshot exists."));
3107 3108 case BE_ERR_BOOTFILE_INST:
3108 3109 return (gettext("Error installing boot files."));
3109 3110 case BE_ERR_EXTCMD:
3110 3111 return (gettext("Error running an external command."));
3111 3112 default:
3112 3113 return (NULL);
3113 3114 }
3114 3115 }
3115 3116
3116 3117 /*
3117 3118 * Function: be_has_grub
3118 3119 * Description: Boolean function indicating whether the current system
3119 3120 * uses grub.
3120 3121 * Return: B_FALSE - the system does not have grub
3121 3122 * B_TRUE - the system does have grub.
3122 3123 * Scope:
3123 3124 * Semi-private (library wide use only)
3124 3125 */
3125 3126 boolean_t
3126 3127 be_has_grub(void)
3127 3128 {
3128 3129 static struct be_defaults be_defaults;
3129 3130 static boolean_t be_deflts_set = B_FALSE;
3130 3131
3131 3132 /* Cache the defaults, because be_has_grub is used often. */
3132 3133 if (be_deflts_set == B_FALSE) {
3133 3134 be_get_defaults(&be_defaults);
3134 3135 be_deflts_set = B_TRUE;
3135 3136 }
3136 3137
3137 3138 return (be_defaults.be_deflt_grub);
3138 3139 }
3139 3140
3140 3141 /*
3141 3142 * Function: be_is_isa
3142 3143 * Description: Boolean function indicating whether the instruction set
3143 3144 * architecture of the executing system matches the name provided.
3144 3145 * The string must match a system defined architecture (e.g.
3145 3146 * "i386", "sparc") and is case sensitive.
3146 3147 * Parameters: name - string representing the name of instruction set
3147 3148 * architecture being tested
3148 3149 * Returns: B_FALSE - the system instruction set architecture is different
3149 3150 * from the one specified
3150 3151 * B_TRUE - the system instruction set architecture is the same
3151 3152 * as the one specified
3152 3153 * Scope:
3153 3154 * Semi-private (library wide use only)
3154 3155 */
3155 3156 boolean_t
3156 3157 be_is_isa(char *name)
3157 3158 {
3158 3159 return ((strcmp((char *)be_get_default_isa(), name) == 0));
3159 3160 }
3160 3161
3161 3162 /*
3162 3163 * Function: be_get_default_isa
3163 3164 * Description:
3164 3165 * Returns the default instruction set architecture of the
3165 3166 * machine it is executed on. (eg. sparc, i386, ...)
3166 3167 * NOTE: SYS_INST environment variable may override default
3167 3168 * return value
3168 3169 * Parameters:
3169 3170 * none
3170 3171 * Returns:
3171 3172 * NULL - the architecture returned by sysinfo() was too
3172 3173 * long for local variables
3173 3174 * char * - pointer to a string containing the default
3174 3175 * implementation
3175 3176 * Scope:
3176 3177 * Semi-private (library wide use only)
3177 3178 */
3178 3179 char *
3179 3180 be_get_default_isa(void)
3180 3181 {
3181 3182 int i;
3182 3183 char *envp;
3183 3184 static char default_inst[ARCH_LENGTH] = "";
3184 3185
3185 3186 if (default_inst[0] == '\0') {
3186 3187 if ((envp = getenv("SYS_INST")) != NULL) {
3187 3188 if ((int)strlen(envp) >= ARCH_LENGTH)
3188 3189 return (NULL);
3189 3190 else
3190 3191 (void) strcpy(default_inst, envp);
3191 3192 } else {
3192 3193 i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH);
3193 3194 if (i < 0 || i > ARCH_LENGTH)
3194 3195 return (NULL);
3195 3196 }
3196 3197 }
3197 3198 return (default_inst);
3198 3199 }
3199 3200
3200 3201 /*
3201 3202 * Function: be_get_platform
3202 3203 * Description:
3203 3204 * Returns the platfom name
3204 3205 * Parameters:
3205 3206 * none
3206 3207 * Returns:
3207 3208 * NULL - the platform name returned by sysinfo() was too
3208 3209 * long for local variables
3209 3210 * char * - pointer to a string containing the platform name
3210 3211 * Scope:
3211 3212 * Semi-private (library wide use only)
3212 3213 */
3213 3214 char *
3214 3215 be_get_platform(void)
3215 3216 {
3216 3217 int i;
3217 3218 static char default_inst[ARCH_LENGTH] = "";
3218 3219
3219 3220 if (default_inst[0] == '\0') {
3220 3221 i = sysinfo(SI_PLATFORM, default_inst, ARCH_LENGTH);
3221 3222 if (i < 0 || i > ARCH_LENGTH)
3222 3223 return (NULL);
3223 3224 }
3224 3225 return (default_inst);
3225 3226 }
3226 3227
3227 3228 /*
3228 3229 * Function: be_run_cmd
3229 3230 * Description:
3230 3231 * Runs a command in a separate subprocess. Splits out stdout from stderr
3231 3232 * and sends each to its own buffer. Buffers must be pre-allocated and
3232 3233 * passed in as arguments. Buffer sizes are also passed in as arguments.
3233 3234 *
3234 3235 * Notes / caveats:
3235 3236 * - Command being run is assumed to not have any stdout or stderr
3236 3237 * redirection.
3237 3238 * - Commands which emit total stderr output of greater than PIPE_BUF
3238 3239 * bytes can hang. For such commands, a different implementation
3239 3240 * which uses poll(2) must be used.
3240 3241 * - stdout_buf can be NULL. In this case, stdout_bufsize is ignored, and
3241 3242 * the stream which would have gone to it is sent to the bit
3242 3243 * bucket.
3243 3244 * - stderr_buf cannot be NULL.
3244 3245 * - Only subprocess errors are appended to the stderr_buf. Errors
3245 3246 * running the command are reported through be_print_err().
3246 3247 * - Data which would overflow its respective buffer is sent to the bit
3247 3248 * bucket.
3248 3249 *
3249 3250 * Parameters:
3250 3251 * command: command to run. Assumed not to have embedded stdout
3251 3252 * or stderr redirection. May have stdin redirection,
3252 3253 * however.
3253 3254 * stderr_buf: buffer returning subprocess stderr data. Errors
3254 3255 * reported by this function are reported through
3255 3256 * be_print_err().
3256 3257 * stderr_bufsize: size of stderr_buf
3257 3258 * stdout_buf: buffer returning subprocess stdout data.
3258 3259 * stdout_bufsize: size of stdout_buf
3259 3260 * Returns:
3260 3261 * BE_SUCCESS - The command ran successfully without returning
3261 3262 * errors.
3262 3263 * BE_ERR_EXTCMD
3263 3264 * - The command could not be run.
3264 3265 * - The command terminated with error status.
3265 3266 * - There were errors extracting or returning subprocess
3266 3267 * data.
3267 3268 * BE_ERR_NOMEM - The command exceeds the command buffer size.
3268 3269 * BE_ERR_INVAL - An invalid argument was specified.
3269 3270 * Scope:
3270 3271 * Semi-private (library wide use only)
3271 3272 */
3272 3273 int
3273 3274 be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize,
3274 3275 char *stdout_buf, int stdout_bufsize)
3275 3276 {
3276 3277 char *temp_filename = strdup(tmpnam(NULL));
3277 3278 FILE *stdout_str = NULL;
3278 3279 FILE *stderr_str = NULL;
3279 3280 char cmdline[BUFSIZ];
3280 3281 char oneline[BUFSIZ];
3281 3282 int exit_status;
3282 3283 int rval = BE_SUCCESS;
3283 3284
3284 3285 if ((command == NULL) || (stderr_buf == NULL) ||
3285 3286 (stderr_bufsize <= 0) || (stdout_bufsize < 0) ||
3286 3287 ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) {
3287 3288 return (BE_ERR_INVAL);
3288 3289 }
3289 3290
3290 3291 /* Set up command so popen returns stderr, not stdout */
3291 3292 if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
3292 3293 temp_filename) >= BUFSIZ) {
3293 3294 rval = BE_ERR_NOMEM;
3294 3295 goto cleanup;
3295 3296 }
3296 3297
3297 3298 /* Set up the fifo that will make stderr available. */
3298 3299 if (mkfifo(temp_filename, 0600) != 0) {
3299 3300 (void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"),
3300 3301 strerror(errno));
3301 3302 rval = BE_ERR_EXTCMD;
3302 3303 goto cleanup;
3303 3304 }
3304 3305
3305 3306 if ((stdout_str = popen(cmdline, "r")) == NULL) {
3306 3307 (void) be_print_err(gettext("be_run_cmd: popen: %s\n"),
3307 3308 strerror(errno));
3308 3309 rval = BE_ERR_EXTCMD;
3309 3310 goto cleanup;
3310 3311 }
3311 3312
3312 3313 if ((stderr_str = fopen(temp_filename, "r")) == NULL) {
3313 3314 (void) be_print_err(gettext("be_run_cmd: fopen: %s\n"),
3314 3315 strerror(errno));
3315 3316 (void) pclose(stdout_str);
3316 3317 rval = BE_ERR_EXTCMD;
3317 3318 goto cleanup;
3318 3319 }
3319 3320
3320 3321 /* Read stdout first, as it usually outputs more than stderr. */
3321 3322 oneline[BUFSIZ-1] = '\0';
3322 3323 while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) {
3323 3324 if (stdout_str != NULL) {
3324 3325 (void) strlcat(stdout_buf, oneline, stdout_bufsize);
3325 3326 }
3326 3327 }
3327 3328
3328 3329 while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) {
3329 3330 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3330 3331 }
3331 3332
3332 3333 /* Close pipe, get exit status. */
3333 3334 if ((exit_status = pclose(stdout_str)) == -1) {
3334 3335 (void) be_print_err(gettext("be_run_cmd: pclose: %s\n"),
3335 3336 strerror(errno));
3336 3337 rval = BE_ERR_EXTCMD;
3337 3338 } else if (WIFEXITED(exit_status)) {
3338 3339 exit_status = (int)((char)WEXITSTATUS(exit_status));
3339 3340 /*
3340 3341 * error code BC_NOUPDT means more recent version
3341 3342 * is installed
3342 3343 */
3343 3344 if (exit_status != BC_SUCCESS && exit_status != BC_NOUPDT) {
3344 3345 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: "
3345 3346 "command terminated with error status: %d\n"),
3346 3347 exit_status);
3347 3348 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3348 3349 rval = BE_ERR_EXTCMD;
3349 3350 }
3350 3351 } else {
3351 3352 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command "
3352 3353 "terminated on signal: %s\n"),
3353 3354 strsignal(WTERMSIG(exit_status)));
3354 3355 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3355 3356 rval = BE_ERR_EXTCMD;
3356 3357 }
3357 3358
3358 3359 cleanup:
3359 3360 (void) unlink(temp_filename);
3360 3361 (void) free(temp_filename);
3361 3362
3362 3363 return (rval);
3363 3364 }
3364 3365
3365 3366 /* ******************************************************************** */
3366 3367 /* Private Functions */
3367 3368 /* ******************************************************************** */
3368 3369
3369 3370 /*
3370 3371 * Function: update_dataset
3371 3372 * Description: This function takes a dataset name and replaces the zpool
3372 3373 * and be_name components of the dataset with the new be_name
3373 3374 * zpool passed in.
3374 3375 * Parameters:
3375 3376 * dataset - name of dataset
3376 3377 * dataset_len - lenth of buffer in which dataset is passed in.
3377 3378 * be_name - name of new BE name to update to.
3378 3379 * old_rc_loc - dataset under which the root container dataset
3379 3380 * for the old BE lives.
3380 3381 * new_rc_loc - dataset under which the root container dataset
3381 3382 * for the new BE lives.
3382 3383 * Returns:
3383 3384 * BE_SUCCESS - Success
3384 3385 * be_errno_t - Failure
3385 3386 * Scope:
3386 3387 * Private
3387 3388 */
3388 3389 static int
3389 3390 update_dataset(char *dataset, int dataset_len, char *be_name,
3390 3391 char *old_rc_loc, char *new_rc_loc)
3391 3392 {
3392 3393 char *ds = NULL;
3393 3394 char *sub_ds = NULL;
3394 3395
3395 3396 /* Tear off the BE container dataset */
3396 3397 if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
3397 3398 return (BE_ERR_INVAL);
3398 3399 }
3399 3400
3400 3401 /* Get dataset name relative to BE root, if there is one */
3401 3402 sub_ds = strchr(ds, '/');
3402 3403
3403 3404 /* Generate the BE root dataset name */
3404 3405 be_make_root_ds(new_rc_loc, be_name, dataset, dataset_len);
3405 3406
3406 3407 /* If a subordinate dataset name was found, append it */
3407 3408 if (sub_ds != NULL)
3408 3409 (void) strlcat(dataset, sub_ds, dataset_len);
3409 3410
3410 3411 free(ds);
3411 3412 return (BE_SUCCESS);
3412 3413 }
3413 3414
3414 3415 /*
3415 3416 * Function: _update_vfstab
3416 3417 * Description: This function updates a vfstab file to reflect the new
3417 3418 * root container dataset location and be_name for all
3418 3419 * entries listed in the be_fs_list_data_t structure passed in.
3419 3420 * Parameters:
3420 3421 * vfstab - vfstab file to modify
3421 3422 * be_name - name of BE to update.
3422 3423 * old_rc_loc - dataset under which the root container dataset
3423 3424 * of the old BE resides in.
3424 3425 * new_rc_loc - dataset under which the root container dataset
3425 3426 * of the new BE resides in.
3426 3427 * fld - be_fs_list_data_t pointer providing the list of
3427 3428 * file systems to look for in vfstab.
3428 3429 * Returns:
3429 3430 * BE_SUCCESS - Success
3430 3431 * be_errno_t - Failure
3431 3432 * Scope:
3432 3433 * Private
3433 3434 */
3434 3435 static int
3435 3436 _update_vfstab(char *vfstab, char *be_name, char *old_rc_loc,
3436 3437 char *new_rc_loc, be_fs_list_data_t *fld)
3437 3438 {
3438 3439 struct vfstab vp;
3439 3440 char *tmp_vfstab = NULL;
3440 3441 char comments_buf[BUFSIZ];
3441 3442 FILE *comments = NULL;
3442 3443 FILE *vfs_ents = NULL;
3443 3444 FILE *tfile = NULL;
3444 3445 struct stat sb;
3445 3446 char dev[MAXPATHLEN];
3446 3447 char *c;
3447 3448 int fd;
3448 3449 int ret = BE_SUCCESS, err = 0;
3449 3450 int i;
3450 3451 int tmp_vfstab_len = 0;
3451 3452
3452 3453 errno = 0;
3453 3454
3454 3455 /*
3455 3456 * Open vfstab for reading twice. First is for comments,
3456 3457 * second is for actual entries.
3457 3458 */
3458 3459 if ((comments = fopen(vfstab, "r")) == NULL ||
3459 3460 (vfs_ents = fopen(vfstab, "r")) == NULL) {
3460 3461 err = errno;
3461 3462 be_print_err(gettext("_update_vfstab: "
3462 3463 "failed to open vfstab (%s): %s\n"), vfstab,
3463 3464 strerror(err));
3464 3465 ret = errno_to_be_err(err);
3465 3466 goto cleanup;
3466 3467 }
3467 3468
3468 3469 /* Grab the stats of the original vfstab file */
3469 3470 if (stat(vfstab, &sb) != 0) {
3470 3471 err = errno;
3471 3472 be_print_err(gettext("_update_vfstab: "
3472 3473 "failed to stat file %s: %s\n"), vfstab,
3473 3474 strerror(err));
3474 3475 ret = errno_to_be_err(err);
3475 3476 goto cleanup;
3476 3477 }
3477 3478
3478 3479 /* Create tmp file for modified vfstab */
3479 3480 if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7))
3480 3481 == NULL) {
3481 3482 be_print_err(gettext("_update_vfstab: "
3482 3483 "malloc failed\n"));
3483 3484 ret = BE_ERR_NOMEM;
3484 3485 goto cleanup;
3485 3486 }
3486 3487 tmp_vfstab_len = strlen(vfstab) + 7;
3487 3488 (void) memset(tmp_vfstab, 0, tmp_vfstab_len);
3488 3489 (void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len);
3489 3490 (void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len);
3490 3491 if ((fd = mkstemp(tmp_vfstab)) == -1) {
3491 3492 err = errno;
3492 3493 be_print_err(gettext("_update_vfstab: "
3493 3494 "mkstemp failed: %s\n"), strerror(err));
3494 3495 ret = errno_to_be_err(err);
3495 3496 goto cleanup;
3496 3497 }
3497 3498 if ((tfile = fdopen(fd, "w")) == NULL) {
3498 3499 err = errno;
3499 3500 be_print_err(gettext("_update_vfstab: "
3500 3501 "could not open file for write\n"));
3501 3502 (void) close(fd);
3502 3503 ret = errno_to_be_err(err);
3503 3504 goto cleanup;
3504 3505 }
3505 3506
3506 3507 while (fgets(comments_buf, BUFSIZ, comments)) {
3507 3508 for (c = comments_buf; *c != '\0' && isspace(*c); c++)
3508 3509 ;
3509 3510 if (*c == '\0') {
3510 3511 continue;
3511 3512 } else if (*c == '#') {
3512 3513 /*
3513 3514 * If line is a comment line, just put
3514 3515 * it through to the tmp vfstab.
3515 3516 */
3516 3517 (void) fputs(comments_buf, tfile);
3517 3518 } else {
3518 3519 /*
3519 3520 * Else line is a vfstab entry, grab it
3520 3521 * into a vfstab struct.
3521 3522 */
3522 3523 if (getvfsent(vfs_ents, &vp) != 0) {
3523 3524 err = errno;
3524 3525 be_print_err(gettext("_update_vfstab: "
3525 3526 "getvfsent failed: %s\n"), strerror(err));
3526 3527 ret = errno_to_be_err(err);
3527 3528 goto cleanup;
3528 3529 }
3529 3530
3530 3531 if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) {
3531 3532 (void) putvfsent(tfile, &vp);
3532 3533 continue;
3533 3534 }
3534 3535
3535 3536 /*
3536 3537 * If the entry is one of the entries in the list
3537 3538 * of file systems to update, modify it's device
3538 3539 * field to be correct for this BE.
3539 3540 */
3540 3541 for (i = 0; i < fld->fs_num; i++) {
3541 3542 if (strcmp(vp.vfs_special, fld->fs_list[i])
3542 3543 == 0) {
3543 3544 /*
3544 3545 * Found entry that needs an update.
3545 3546 * Replace the root container dataset
3546 3547 * location and be_name in the
3547 3548 * entry's device.
3548 3549 */
3549 3550 (void) strlcpy(dev, vp.vfs_special,
3550 3551 sizeof (dev));
3551 3552
3552 3553 if ((ret = update_dataset(dev,
3553 3554 sizeof (dev), be_name, old_rc_loc,
3554 3555 new_rc_loc)) != 0) {
3555 3556 be_print_err(
3556 3557 gettext("_update_vfstab: "
3557 3558 "Failed to update device "
3558 3559 "field for vfstab entry "
3559 3560 "%s\n"), fld->fs_list[i]);
3560 3561 goto cleanup;
3561 3562 }
3562 3563
3563 3564 vp.vfs_special = dev;
3564 3565 break;
3565 3566 }
3566 3567 }
3567 3568
3568 3569 /* Put entry through to tmp vfstab */
3569 3570 (void) putvfsent(tfile, &vp);
3570 3571 }
3571 3572 }
3572 3573
3573 3574 (void) fclose(comments);
3574 3575 comments = NULL;
3575 3576 (void) fclose(vfs_ents);
3576 3577 vfs_ents = NULL;
3577 3578 (void) fclose(tfile);
3578 3579 tfile = NULL;
3579 3580
3580 3581 /* Copy tmp vfstab into place */
3581 3582 if (rename(tmp_vfstab, vfstab) != 0) {
3582 3583 err = errno;
3583 3584 be_print_err(gettext("_update_vfstab: "
3584 3585 "failed to rename file %s to %s: %s\n"), tmp_vfstab,
3585 3586 vfstab, strerror(err));
3586 3587 ret = errno_to_be_err(err);
3587 3588 goto cleanup;
3588 3589 }
3589 3590
3590 3591 /* Set the perms and ownership of the updated file */
3591 3592 if (chmod(vfstab, sb.st_mode) != 0) {
3592 3593 err = errno;
3593 3594 be_print_err(gettext("_update_vfstab: "
3594 3595 "failed to chmod %s: %s\n"), vfstab, strerror(err));
3595 3596 ret = errno_to_be_err(err);
3596 3597 goto cleanup;
3597 3598 }
3598 3599 if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) {
3599 3600 err = errno;
3600 3601 be_print_err(gettext("_update_vfstab: "
3601 3602 "failed to chown %s: %s\n"), vfstab, strerror(err));
3602 3603 ret = errno_to_be_err(err);
3603 3604 goto cleanup;
3604 3605 }
3605 3606
3606 3607 cleanup:
3607 3608 if (comments != NULL)
3608 3609 (void) fclose(comments);
3609 3610 if (vfs_ents != NULL)
3610 3611 (void) fclose(vfs_ents);
3611 3612 (void) unlink(tmp_vfstab);
3612 3613 (void) free(tmp_vfstab);
3613 3614 if (tfile != NULL)
3614 3615 (void) fclose(tfile);
3615 3616
3616 3617 return (ret);
3617 3618 }
3618 3619
3619 3620
3620 3621 /*
3621 3622 * Function: be_get_auto_name
3622 3623 * Description: Generate an auto name constructed based on the BE name
3623 3624 * of the original BE or zone BE being cloned.
3624 3625 * Parameters:
3625 3626 * obe_name - name of the original BE or zone BE being cloned.
3626 3627 * container_ds - container dataset for the zone.
3627 3628 * Note: if zone_be is false this should be
3628 3629 * NULL.
3629 3630 * zone_be - flag that indicates if we are operating on a zone BE.
3630 3631 * Returns:
3631 3632 * Success - pointer to auto generated BE name. The name
3632 3633 * is allocated in heap storage so the caller is
3633 3634 * responsible for free'ing the name.
3634 3635 * Failure - NULL
3635 3636 * Scope:
3636 3637 * Private
3637 3638 */
3638 3639 static char *
3639 3640 be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be)
3640 3641 {
3641 3642 be_node_list_t *be_nodes = NULL;
3642 3643 be_node_list_t *cur_be = NULL;
3643 3644 char auto_be_name[MAXPATHLEN];
3644 3645 char base_be_name[MAXPATHLEN];
3645 3646 char cur_be_name[MAXPATHLEN];
3646 3647 char *num_str = NULL;
3647 3648 char *c = NULL;
3648 3649 int num = 0;
3649 3650 int cur_num = 0;
3650 3651
3651 3652 errno = 0;
3652 3653
3653 3654 /*
3654 3655 * Check if obe_name is already in an auto BE name format.
3655 3656 * If it is, then strip off the increment number to get the
3656 3657 * base name.
3657 3658 */
3658 3659 (void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));
3659 3660
3660 3661 if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
3661 3662 != NULL) {
3662 3663 /* Make sure remaining string is all digits */
3663 3664 c = num_str + 1;
3664 3665 while (c[0] != '\0' && isdigit(c[0]))
3665 3666 c++;
3666 3667 /*
3667 3668 * If we're now at the end of the string strip off the
3668 3669 * increment number.
3669 3670 */
3670 3671 if (c[0] == '\0')
3671 3672 num_str[0] = '\0';
3672 3673 }
3673 3674
3674 3675 if (zone_be) {
3675 3676 if (be_container_ds == NULL)
3676 3677 return (NULL);
3677 3678 if (be_get_zone_be_list(obe_name, be_container_ds,
3678 3679 &be_nodes) != BE_SUCCESS) {
3679 3680 be_print_err(gettext("be_get_auto_name: "
3680 3681 "be_get_zone_be_list failed\n"));
3681 3682 return (NULL);
3682 3683 }
3683 3684 } else if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS) {
3684 3685 be_print_err(gettext("be_get_auto_name: be_list failed\n"));
3685 3686 return (NULL);
3686 3687 }
3687 3688
3688 3689 for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
3689 3690 (void) strlcpy(cur_be_name, cur_be->be_node_name,
3690 3691 sizeof (cur_be_name));
3691 3692
3692 3693 /* If cur_be_name doesn't match at least base be name, skip. */
3693 3694 if (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
3694 3695 != 0)
3695 3696 continue;
3696 3697
3697 3698 /* Get the string following the base be name */
3698 3699 num_str = cur_be_name + strlen(base_be_name);
3699 3700
3700 3701 /*
3701 3702 * If nothing follows the base be name, this cur_be_name
3702 3703 * is the BE named with the base be name, skip.
3703 3704 */
3704 3705 if (num_str == NULL || num_str[0] == '\0')
3705 3706 continue;
3706 3707
3707 3708 /*
3708 3709 * Remove the name delimiter. If its not there,
3709 3710 * cur_be_name isn't part of this BE name stream, skip.
3710 3711 */
3711 3712 if (num_str[0] == BE_AUTO_NAME_DELIM)
3712 3713 num_str++;
3713 3714 else
3714 3715 continue;
3715 3716
3716 3717 /* Make sure remaining string is all digits */
3717 3718 c = num_str;
3718 3719 while (c[0] != '\0' && isdigit(c[0]))
3719 3720 c++;
3720 3721 if (c[0] != '\0')
3721 3722 continue;
3722 3723
3723 3724 /* Convert the number string to an int */
3724 3725 cur_num = atoi(num_str);
3725 3726
3726 3727 /*
3727 3728 * If failed to convert the string, skip it. If its too
3728 3729 * long to be converted to an int, we wouldn't auto generate
3729 3730 * this number anyway so there couldn't be a conflict.
3730 3731 * We treat it as a manually created BE name.
3731 3732 */
3732 3733 if (cur_num == 0 && errno == EINVAL)
3733 3734 continue;
3734 3735
3735 3736 /*
3736 3737 * Compare current number to current max number,
3737 3738 * take higher of the two.
3738 3739 */
3739 3740 if (cur_num > num)
3740 3741 num = cur_num;
3741 3742 }
3742 3743
3743 3744 /*
3744 3745 * Store off a copy of 'num' incase we need it later. If incrementing
3745 3746 * 'num' causes it to roll over, this means 'num' is the largest
3746 3747 * positive int possible; we'll need it later in the loop to determine
3747 3748 * if we've exhausted all possible increment numbers. We store it in
3748 3749 * 'cur_num'.
3749 3750 */
3750 3751 cur_num = num;
3751 3752
3752 3753 /* Increment 'num' to get new auto BE name number */
3753 3754 if (++num <= 0) {
3754 3755 int ret = 0;
3755 3756
3756 3757 /*
3757 3758 * Since incrementing 'num' caused it to rollover, start
3758 3759 * over at 0 and find the first available number.
3759 3760 */
3760 3761 for (num = 0; num < cur_num; num++) {
3761 3762
3762 3763 (void) snprintf(cur_be_name, sizeof (cur_be_name),
3763 3764 "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num);
3764 3765
3765 3766 ret = zpool_iter(g_zfs, be_exists_callback,
3766 3767 cur_be_name);
3767 3768
3768 3769 if (ret == 0) {
3769 3770 /*
3770 3771 * BE name doesn't exist, break out
3771 3772 * to use 'num'.
3772 3773 */
3773 3774 break;
3774 3775 } else if (ret == 1) {
3775 3776 /* BE name exists, continue looking */
3776 3777 continue;
3777 3778 } else {
3778 3779 be_print_err(gettext("be_get_auto_name: "
3779 3780 "zpool_iter failed: %s\n"),
3780 3781 libzfs_error_description(g_zfs));
3781 3782 be_free_list(be_nodes);
3782 3783 return (NULL);
3783 3784 }
3784 3785 }
3785 3786
3786 3787 /*
3787 3788 * If 'num' equals 'cur_num', we've exhausted all possible
3788 3789 * auto BE names for this base BE name.
3789 3790 */
3790 3791 if (num == cur_num) {
3791 3792 be_print_err(gettext("be_get_auto_name: "
3792 3793 "No more available auto BE names for base "
3793 3794 "BE name %s\n"), base_be_name);
3794 3795 be_free_list(be_nodes);
3795 3796 return (NULL);
3796 3797 }
3797 3798 }
3798 3799
3799 3800 be_free_list(be_nodes);
3800 3801
3801 3802 /*
3802 3803 * Generate string for auto BE name.
3803 3804 */
3804 3805 (void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d",
3805 3806 base_be_name, BE_AUTO_NAME_DELIM, num);
3806 3807
3807 3808 if ((c = strdup(auto_be_name)) == NULL) {
3808 3809 be_print_err(gettext("be_get_auto_name: "
3809 3810 "memory allocation failed\n"));
3810 3811 return (NULL);
3811 3812 }
3812 3813
3813 3814 return (c);
3814 3815 }
3815 3816
3816 3817 /*
3817 3818 * Function: be_get_console_prop
3818 3819 * Description: Determine console device.
3819 3820 * Returns:
3820 3821 * Success - pointer to console setting.
3821 3822 * Failure - NULL
3822 3823 * Scope:
3823 3824 * Private
3824 3825 */
3825 3826 static char *
3826 3827 be_get_console_prop(void)
3827 3828 {
3828 3829 di_node_t dn;
3829 3830 char *console = NULL;
3830 3831
3831 3832 if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
3832 3833 be_print_err(gettext("be_get_console_prop: "
3833 3834 "di_init() failed\n"));
3834 3835 return (NULL);
3835 3836 }
3836 3837
3837 3838 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3838 3839 "console", &console) != -1) {
3839 3840 di_fini(dn);
3840 3841 return (console);
3841 3842 }
3842 3843
3843 3844 if (console == NULL) {
3844 3845 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3845 3846 "output-device", &console) != -1) {
3846 3847 di_fini(dn);
3847 3848 if (strncmp(console, "screen", strlen("screen")) == 0)
3848 3849 console = BE_DEFAULT_CONSOLE;
3849 3850 }
3850 3851 }
3851 3852
3852 3853 /*
3853 3854 * Default console to text
3854 3855 */
3855 3856 if (console == NULL) {
3856 3857 console = BE_DEFAULT_CONSOLE;
3857 3858 }
3858 3859
3859 3860 return (console);
3860 3861 }
3861 3862
3862 3863 /*
3863 3864 * Function: be_create_menu
3864 3865 * Description:
3865 3866 * This function is used if no menu.lst file exists. In
3866 3867 * this case a new file is created and if needed default
3867 3868 * lines are added to the file.
3868 3869 * Parameters:
3869 3870 * pool - The name of the pool the menu.lst file is on
3870 3871 * menu_file - The name of the file we're creating.
3871 3872 * menu_fp - A pointer to the file pointer of the file we
3872 3873 * created. This is also used to pass back the file
3873 3874 * pointer to the newly created file.
3874 3875 * mode - the original mode used for the failed attempt to
3875 3876 * non-existent file.
3876 3877 * Returns:
3877 3878 * BE_SUCCESS - Success
3878 3879 * be_errno_t - Failure
3879 3880 * Scope:
3880 3881 * Private
3881 3882 */
3882 3883 static int
3883 3884 be_create_menu(
3884 3885 char *pool,
3885 3886 char *menu_file,
3886 3887 FILE **menu_fp,
3887 3888 char *mode)
3888 3889 {
3889 3890 be_node_list_t *be_nodes = NULL;
3890 3891 char *menu_path = NULL;
3891 3892 char *be_rpool = NULL;
3892 3893 char *be_name = NULL;
3893 3894 char *console = NULL;
3894 3895 errno = 0;
3895 3896
3896 3897 if (menu_file == NULL || menu_fp == NULL || mode == NULL)
3897 3898 return (BE_ERR_INVAL);
3898 3899
3899 3900 menu_path = strdup(menu_file);
3900 3901 if (menu_path == NULL)
↓ open down ↓ |
3617 lines elided |
↑ open up ↑ |
3901 3902 return (BE_ERR_NOMEM);
3902 3903
3903 3904 (void) dirname(menu_path);
3904 3905 if (*menu_path == '.') {
3905 3906 free(menu_path);
3906 3907 return (BE_ERR_BAD_MENU_PATH);
3907 3908 }
3908 3909 if (mkdirp(menu_path,
3909 3910 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
3910 3911 errno != EEXIST) {
3911 - free(menu_path);
3912 3912 be_print_err(gettext("be_create_menu: Failed to create the %s "
3913 3913 "directory: %s\n"), menu_path, strerror(errno));
3914 + free(menu_path);
3914 3915 return (errno_to_be_err(errno));
3915 3916 }
3916 3917 free(menu_path);
3917 3918
3918 3919 /*
3919 3920 * Check to see if this system supports grub
3920 3921 */
3921 3922 if (be_has_grub()) {
3922 3923 /*
3923 3924 * The grub menu is missing so we need to create it
3924 3925 * and fill in the first few lines.
3925 3926 */
3926 3927 FILE *temp_fp = fopen(menu_file, "a+");
3927 3928 if (temp_fp == NULL) {
3928 3929 *menu_fp = NULL;
3929 3930 return (errno_to_be_err(errno));
3930 3931 }
3931 3932
3932 3933 if ((console = be_get_console_prop()) != NULL) {
3933 3934
3934 3935 /*
3935 3936 * If console is redirected to serial line,
3936 3937 * GRUB splash screen will not be enabled.
3937 3938 */
3938 3939 if (strncmp(console, "text", strlen("text")) == 0 ||
3939 3940 strncmp(console, "graphics",
3940 3941 strlen("graphics")) == 0) {
3941 3942
3942 3943 (void) fprintf(temp_fp, "%s\n", BE_GRUB_SPLASH);
3943 3944 (void) fprintf(temp_fp, "%s\n",
3944 3945 BE_GRUB_FOREGROUND);
3945 3946 (void) fprintf(temp_fp, "%s\n",
3946 3947 BE_GRUB_BACKGROUND);
3947 3948 (void) fprintf(temp_fp, "%s\n",
3948 3949 BE_GRUB_DEFAULT);
3949 3950 } else {
3950 3951 be_print_err(gettext("be_create_menu: "
3951 3952 "console on serial line, "
3952 3953 "GRUB splash image will be disabled\n"));
3953 3954 }
3954 3955 }
3955 3956
3956 3957 (void) fprintf(temp_fp, "timeout 30\n");
3957 3958 (void) fclose(temp_fp);
3958 3959
3959 3960 } else {
3960 3961 /*
3961 3962 * The menu file doesn't exist so we need to create a
3962 3963 * blank file.
3963 3964 */
3964 3965 FILE *temp_fp = fopen(menu_file, "w+");
3965 3966 if (temp_fp == NULL) {
3966 3967 *menu_fp = NULL;
3967 3968 return (errno_to_be_err(errno));
3968 3969 }
3969 3970 (void) fclose(temp_fp);
3970 3971 }
3971 3972
3972 3973 /*
3973 3974 * Now we need to add all the BE's back into the the file.
3974 3975 */
3975 3976 if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) == BE_SUCCESS) {
3976 3977 while (be_nodes != NULL) {
3977 3978 if (strcmp(pool, be_nodes->be_rpool) == 0) {
3978 3979 (void) be_append_menu(be_nodes->be_node_name,
3979 3980 be_nodes->be_rpool, NULL, NULL, NULL);
3980 3981 }
3981 3982 if (be_nodes->be_active_on_boot) {
3982 3983 be_rpool = strdup(be_nodes->be_rpool);
3983 3984 be_name = strdup(be_nodes->be_node_name);
3984 3985 }
3985 3986
3986 3987 be_nodes = be_nodes->be_next_node;
3987 3988 }
3988 3989 }
3989 3990 be_free_list(be_nodes);
3990 3991
3991 3992 /*
3992 3993 * Check to see if this system supports grub
3993 3994 */
3994 3995 if (be_has_grub()) {
3995 3996 int err = be_change_grub_default(be_name, be_rpool);
3996 3997 if (err != BE_SUCCESS)
3997 3998 return (err);
3998 3999 }
3999 4000 *menu_fp = fopen(menu_file, mode);
4000 4001 if (*menu_fp == NULL)
4001 4002 return (errno_to_be_err(errno));
4002 4003
4003 4004 return (BE_SUCCESS);
4004 4005 }
4005 4006
4006 4007 /*
4007 4008 * Function: be_open_menu
4008 4009 * Description:
4009 4010 * This function is used it open the menu.lst file. If this
4010 4011 * file does not exist be_create_menu is called to create it
4011 4012 * and the open file pointer is returned. If the file does
4012 4013 * exist it is simply opened using the mode passed in.
4013 4014 * Parameters:
4014 4015 * pool - The name of the pool the menu.lst file is on
4015 4016 * menu_file - The name of the file we're opening.
4016 4017 * menu_fp - A pointer to the file pointer of the file we're
4017 4018 * opening. This is also used to pass back the file
4018 4019 * pointer.
4019 4020 * mode - the original mode to be used for opening the menu.lst
4020 4021 * file.
4021 4022 * create_menu - If this is true and the menu.lst file does not
4022 4023 * exist we will attempt to re-create it. However
4023 4024 * if it's false the error returned from the fopen
4024 4025 * will be returned.
4025 4026 * Returns:
4026 4027 * BE_SUCCESS - Success
4027 4028 * be_errno_t - Failure
4028 4029 * Scope:
4029 4030 * Private
4030 4031 */
4031 4032 static int
4032 4033 be_open_menu(
4033 4034 char *pool,
4034 4035 char *menu_file,
4035 4036 FILE **menu_fp,
4036 4037 char *mode,
4037 4038 boolean_t create_menu)
4038 4039 {
4039 4040 int err = 0;
4040 4041 boolean_t set_print = B_FALSE;
4041 4042
4042 4043 *menu_fp = fopen(menu_file, mode);
4043 4044 err = errno;
4044 4045 if (*menu_fp == NULL) {
4045 4046 if (err == ENOENT && create_menu) {
4046 4047 be_print_err(gettext("be_open_menu: menu.lst "
4047 4048 "file %s does not exist,\n"), menu_file);
4048 4049 if (!do_print) {
4049 4050 set_print = B_TRUE;
4050 4051 do_print = B_TRUE;
4051 4052 }
4052 4053 be_print_err(gettext("WARNING: menu.lst "
4053 4054 "file %s does not exist,\n generating "
4054 4055 "a new menu.lst file\n"), menu_file);
4055 4056 if (set_print)
4056 4057 do_print = B_FALSE;
4057 4058 err = 0;
4058 4059 if ((err = be_create_menu(pool, menu_file,
4059 4060 menu_fp, mode)) == ENOENT)
4060 4061 return (BE_ERR_NO_MENU);
4061 4062 else if (err != BE_SUCCESS)
4062 4063 return (err);
4063 4064 else if (*menu_fp == NULL)
4064 4065 return (BE_ERR_NO_MENU);
4065 4066 } else {
4066 4067 be_print_err(gettext("be_open_menu: failed "
4067 4068 "to open menu.lst file %s\n"), menu_file);
4068 4069 if (err == ENOENT)
4069 4070 return (BE_ERR_NO_MENU);
4070 4071 else
4071 4072 return (errno_to_be_err(err));
4072 4073 }
4073 4074 }
4074 4075 return (BE_SUCCESS);
4075 4076 }
↓ open down ↓ |
152 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX