1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 27 * Copyright 2015 Toomas Soome <tsoome@me.com> 28 */ 29 30 /* 31 * This file contains all the functions that manipulate the file 32 * system where the GRUB menu resides. 33 */ 34 #include <stdio.h> 35 #include <errno.h> 36 #include <stdlib.h> 37 #include <strings.h> 38 #include <unistd.h> 39 #include <fcntl.h> 40 #include <assert.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <sys/mount.h> 44 #include <sys/mntent.h> 45 #include <sys/mnttab.h> 46 #include <sys/efi_partition.h> 47 #include <sys/vtoc.h> 48 #include <sys/fs/ufs_mount.h> 49 #include <sys/dktp/fdisk.h> 50 #include <libfstyp.h> 51 #if defined(i386) || defined(__amd64) 52 #include <libfdisk.h> 53 #endif 54 55 #include "libgrub_impl.h" 56 57 static int 58 slice_match(const char *physpath, int slice) 59 { 60 const char *pos; 61 62 /* always match whole disk slice */ 63 if (slice == SLCNUM_WHOLE_DISK) 64 return (0); 65 66 return ((pos = strrchr(physpath, slice)) == NULL || 67 pos[1] != 0 || pos[-1] != ':'); 68 } 69 70 /* 71 * Returns zero if path contains ufs 72 */ 73 static int 74 slice_ufs(const char *path) 75 { 76 int fd, ret; 77 const char *id; 78 fstyp_handle_t hdl; 79 80 fd = open(path, O_RDONLY); 81 if ((ret = fstyp_init(fd, 0, NULL, &hdl)) == 0) { 82 ret = fstyp_ident(hdl, "ufs", &id); 83 fstyp_fini(hdl); 84 } 85 (void) close(fd); 86 return (ret); 87 } 88 89 90 static int 91 get_sol_prtnum(const char *physpath) 92 { 93 int i, fd; 94 char *pos; 95 size_t sz; 96 struct mboot *mb; 97 struct ipart *ipart; 98 char boot_sect[512]; 99 char rdev[MAXNAMELEN]; 100 #if defined(i386) || defined(__amd64) 101 ext_part_t *epp; 102 int ext_part_found = 0; 103 #endif 104 105 (void) snprintf(rdev, sizeof (rdev), "/devices%s,raw", physpath); 106 107 if ((pos = strrchr(rdev, ':')) == NULL) 108 return (PRTNUM_INVALID); 109 110 /* 111 * first check for EFI partitioning, efi_alloc_and_read() 112 * will return partition number. 113 */ 114 if ((fd = open(rdev, O_RDONLY|O_NDELAY)) >= 0) { 115 struct dk_gpt *vtoc; 116 117 if ((i = efi_alloc_and_read(fd, &vtoc)) >= 0) { 118 /* zfs is using V_USR */ 119 if (vtoc->efi_parts[i].p_tag != V_USR) 120 i = PRTNUM_INVALID; /* error */ 121 efi_free(vtoc); 122 (void) close(fd); 123 return (i); 124 } 125 (void) close(fd); 126 } else { 127 return (PRTNUM_INVALID); 128 } 129 130 pos[1] = SLCNUM_WHOLE_DISK; 131 132 fd = open(rdev, O_RDONLY); 133 sz = read(fd, boot_sect, sizeof (boot_sect)); 134 (void) close(fd); 135 136 if (sz != sizeof (boot_sect)) 137 return (PRTNUM_INVALID); 138 139 /* parse fdisk table */ 140 mb = (struct mboot *)(uintptr_t)boot_sect; 141 ipart = (struct ipart *)(uintptr_t)mb->parts; 142 for (i = 0; i < FD_NUMPART; ++i) { 143 if (ipart[i].systid == SUNIXOS || ipart[i].systid == SUNIXOS2) 144 return (i); 145 146 #if defined(i386) || defined(__amd64) 147 if (!fdisk_is_dos_extended(ipart[i].systid) || 148 (ext_part_found == 1)) 149 continue; 150 151 ext_part_found = 1; 152 153 if (libfdisk_init(&epp, rdev, NULL, FDISK_READ_DISK) == 154 FDISK_SUCCESS) { 155 uint32_t begs, nums; 156 int pno; 157 int rval; 158 159 rval = fdisk_get_solaris_part(epp, &pno, &begs, &nums); 160 161 libfdisk_fini(&epp); 162 163 if (rval == FDISK_SUCCESS) 164 return (pno - 1); 165 } 166 #endif 167 } 168 return (PRTNUM_INVALID); 169 } 170 171 /* 172 * Get physpath, topfs and bootfs for ZFS root dataset. 173 * Return 0 on success, non-zero (not errno) on failure. 174 */ 175 static int 176 get_zfs_root(zfs_handle_t *zfh, grub_fs_t *fs, grub_root_t *root) 177 { 178 int ret; 179 zpool_handle_t *zph; 180 const char *name; 181 182 if (zfs_get_type(zfh) != ZFS_TYPE_FILESYSTEM || 183 (name = zfs_get_name(zfh)) == NULL || 184 (zph = zpool_open(fs->gf_lzfh, name)) == NULL) 185 return (-1); 186 187 if ((ret = zpool_get_physpath(zph, root->gr_physpath, 188 sizeof (root->gr_physpath))) == 0 && 189 (ret = zpool_get_prop(zph, ZPOOL_PROP_BOOTFS, 190 root->gr_fs[GRBM_ZFS_BOOTFS].gfs_dev, 191 sizeof (root->gr_fs[GRBM_ZFS_BOOTFS].gfs_dev), NULL, 192 B_FALSE)) == 0) { 193 194 (void) strlcpy(root->gr_fs[GRBM_ZFS_TOPFS].gfs_dev, name, 195 sizeof (root->gr_fs[GRBM_ZFS_TOPFS].gfs_dev)); 196 (void) grub_fsd_get_mountp(root->gr_fs + GRBM_ZFS_BOOTFS, 197 MNTTYPE_ZFS); 198 (void) grub_fsd_get_mountp(root->gr_fs + GRBM_ZFS_TOPFS, 199 MNTTYPE_ZFS); 200 } 201 202 zpool_close(zph); 203 return (ret); 204 } 205 206 /* 207 * On entry physpath parameter supposed to contain: 208 * <disk_physpath>[<space><disk_physpath>]*. 209 * Retrieves first <disk_physpath> that matches both partition and slice. 210 * If any partition and slice is acceptable, first <disk_physpath> is returned. 211 */ 212 static int 213 get_one_physpath(char *physpath, uint_t prtnum, uint_t slcnum) 214 { 215 int ret; 216 char *tmp, *tok; 217 218 if (!IS_SLCNUM_VALID(slcnum) && !IS_PRTNUM_VALID(prtnum)) { 219 (void) strtok(physpath, " "); 220 return (0); 221 } 222 223 if ((tmp = strdup(physpath)) == NULL) 224 return (errno); 225 226 ret = ENODEV; 227 for (tok = strtok(tmp, " "); tok != NULL; tok = strtok(NULL, " ")) { 228 if ((ret = (slice_match(tok, slcnum) != 0 || 229 get_sol_prtnum(tok) != prtnum)) == 0) { 230 (void) strcpy(physpath, tok); 231 break; 232 } 233 } 234 235 free(tmp); 236 if (ret) 237 ret = ENODEV; 238 return (ret); 239 } 240 241 static int 242 zfs_bootsign(zfs_handle_t *zfh, void *data) 243 { 244 grub_barg_t *barg; 245 grub_menu_t *menu; 246 struct stat st; 247 char path[MAXPATHLEN]; 248 249 barg = (grub_barg_t *)data; 250 menu = barg->gb_entry->ge_menu; 251 252 do { 253 if (get_zfs_root(zfh, &menu->gm_fs, &barg->gb_root) != 0 || 254 get_one_physpath(barg->gb_root.gr_physpath, barg->gb_prtnum, 255 barg->gb_slcnum) != 0) 256 break; 257 258 /* 259 * if top zfs dataset is not mounted, mount it now 260 */ 261 if (barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_mountp[0] == 0) { 262 if (grub_fsd_mount_tmp(barg->gb_root.gr_fs + 263 GRBM_ZFS_TOPFS, MNTTYPE_ZFS) != 0) 264 break; 265 } 266 267 /* check that bootsign exists and it is a regular file */ 268 (void) snprintf(path, sizeof (path), "%s%s", 269 barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_mountp, 270 barg->gb_bootsign); 271 272 if (lstat(path, &st) != 0 || S_ISREG(st.st_mode) == 0 || 273 (st.st_mode & S_IRUSR) == 0) 274 break; 275 276 (void) strlcpy(barg->gb_root.gr_fstyp, MNTTYPE_ZFS, 277 sizeof (barg->gb_root.gr_fstyp)); 278 barg->gb_walkret = 0; 279 /* LINTED: E_CONSTANT_CONDITION */ 280 } while (0); 281 282 grub_fsd_umount_tmp(barg->gb_root.gr_fs + GRBM_ZFS_TOPFS); 283 zfs_close(zfh); 284 285 /* return non-zero to terminate the walk */ 286 return (barg->gb_walkret == 0); 287 } 288 289 static int 290 get_devlink(di_devlink_t dl, void *arg) 291 { 292 const char *path; 293 grub_barg_t *barg; 294 295 barg = (grub_barg_t *)arg; 296 if ((path = di_devlink_path(dl)) != NULL) 297 (void) strlcpy(barg->gb_root.gr_fs[GRBM_UFS].gfs_dev, path, 298 sizeof (barg->gb_root.gr_fs[GRBM_UFS].gfs_dev)); 299 return (DI_WALK_TERMINATE); 300 } 301 302 static int 303 ufs_bootsign_check(grub_barg_t *barg) 304 { 305 int ret; 306 struct stat st; 307 grub_menu_t *mp; 308 char path[MAXPATHLEN]; 309 310 mp = barg->gb_entry->ge_menu; 311 312 /* get /dev/dsk link */ 313 if (di_devlink_walk(mp->gm_fs.gf_dvlh, "^dsk/", 314 barg->gb_root.gr_physpath, DI_PRIMARY_LINK, barg, get_devlink) != 0) 315 return (errno); 316 /* 317 * if disk is not mounted, mount it now 318 */ 319 if (grub_fsd_get_mountp(barg->gb_root.gr_fs + GRBM_UFS, 320 MNTTYPE_UFS) != 0) { 321 if ((ret = 322 slice_ufs(barg->gb_root.gr_fs[GRBM_UFS].gfs_dev)) != 0 || 323 (ret = grub_fsd_mount_tmp(barg->gb_root.gr_fs + GRBM_UFS, 324 MNTTYPE_UFS)) != 0) 325 return (ret); 326 } 327 328 (void) snprintf(path, sizeof (path), "%s%s", 329 barg->gb_root.gr_fs[GRBM_UFS].gfs_mountp, barg->gb_bootsign); 330 331 if (lstat(path, &st) == 0 && S_ISREG(st.st_mode) && 332 (st.st_mode & S_IRUSR) != 0) { 333 barg->gb_walkret = 0; 334 (void) strlcpy(barg->gb_root.gr_fstyp, MNTTYPE_UFS, 335 sizeof (barg->gb_root.gr_fstyp)); 336 } 337 338 grub_fsd_umount_tmp(barg->gb_root.gr_fs + GRBM_UFS); 339 return (barg->gb_walkret); 340 } 341 342 static int 343 ufs_bootsign(di_node_t node, di_minor_t minor, void *arg) 344 { 345 uint_t prtnum; 346 char *name, *path; 347 grub_barg_t *barg; 348 349 barg = (grub_barg_t *)arg; 350 351 if (di_minor_spectype(minor) != S_IFBLK) 352 return (DI_WALK_CONTINUE); 353 354 name = di_minor_name(minor); 355 if (name[0] != barg->gb_slcnum || name[1] != 0) 356 return (DI_WALK_CONTINUE); 357 358 path = di_devfs_path(node); 359 (void) snprintf(barg->gb_root.gr_physpath, 360 sizeof (barg->gb_root.gr_physpath), "%s:%c", path, barg->gb_slcnum); 361 di_devfs_path_free(path); 362 363 prtnum = get_sol_prtnum(barg->gb_root.gr_physpath); 364 if (!IS_PRTNUM_VALID(prtnum)) 365 return (DI_WALK_CONTINUE); 366 367 /* 368 * check only specified partition, slice 369 */ 370 371 if (IS_PRTNUM_VALID(barg->gb_prtnum)) { 372 if (prtnum != barg->gb_prtnum || ufs_bootsign_check(barg) != 0) 373 return (DI_WALK_CONTINUE); 374 return (DI_WALK_TERMINATE); 375 } 376 377 /* 378 * Walk through all slices in found solaris partition 379 */ 380 381 barg->gb_prtnum = prtnum; 382 minor = DI_MINOR_NIL; 383 384 while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { 385 386 if (di_minor_spectype(minor) != S_IFBLK) 387 continue; 388 389 name = di_minor_name(minor); 390 if (!IS_SLCNUM_VALID(name[0]) || name[1] != 0) 391 continue; 392 393 barg->gb_slcnum = name[0]; 394 path = strrchr(barg->gb_root.gr_physpath, ':'); 395 path[1] = barg->gb_slcnum; 396 397 if (ufs_bootsign_check(barg) == 0) 398 return (DI_WALK_TERMINATE); 399 } 400 401 barg->gb_prtnum = (uint_t)PRTNUM_INVALID; 402 barg->gb_slcnum = (uint_t)SLCNUM_WHOLE_DISK; 403 return (DI_WALK_CONTINUE); 404 } 405 406 /* 407 * Differs from what GRUB is doing: GRUB searchs through all disks seen by bios 408 * for bootsign, if bootsign is found on ufs slice GRUB sets it as a root, 409 * if on zfs, then GRUB uses zfs slice as root only if bootsign wasn't found 410 * on other slices. 411 * That function first searches through all top datasets of active zpools, 412 * then if bootsign still not found walks through all disks and tries to 413 * find ufs slice with the bootsign. 414 */ 415 int 416 grub_find_bootsign(grub_barg_t *barg) 417 { 418 grub_menu_t *mp; 419 mp = barg->gb_entry->ge_menu; 420 421 /* try to find bootsign over zfs pools */ 422 barg->gb_walkret = EG_BOOTSIGN; 423 (void) zfs_iter_root(mp->gm_fs.gf_lzfh, zfs_bootsign, barg); 424 425 /* try ufs now */ 426 if (barg->gb_walkret != 0 && di_walk_minor(mp->gm_fs.gf_diroot, 427 DDI_NT_BLOCK, 0, barg, ufs_bootsign) != 0) 428 return (errno); 429 430 return (barg->gb_walkret); 431 } 432 433 /* 434 * Get current root file system. 435 * Return 0 on success, errno code on failure. 436 */ 437 int 438 grub_current_root(grub_fs_t *fs, grub_root_t *root) 439 { 440 int rc = 0; 441 FILE *fp = NULL; 442 char *name = NULL; 443 zfs_handle_t *zfh = NULL; 444 struct mnttab mp = {0}; 445 struct mnttab mpref = {0}; 446 char buf[MAXNAMELEN] = {0}; 447 448 mpref.mnt_mountp = "/"; 449 450 if ((fp = fopen(MNTTAB, "r")) == NULL) 451 return (errno); 452 453 /* 454 * getmntany returns non-zero for failure, and sets errno 455 */ 456 rc = getmntany(fp, &mp, &mpref); 457 if (rc != 0) 458 rc = errno; 459 460 (void) fclose(fp); 461 462 if (rc != 0) 463 return (rc); 464 465 (void) strlcpy(root->gr_fstyp, mp.mnt_fstype, sizeof (root->gr_fstyp)); 466 467 if (strcmp(root->gr_fstyp, MNTTYPE_ZFS) == 0) { 468 469 (void) strlcpy(buf, mp.mnt_special, sizeof (buf)); 470 if ((name = strtok(buf, "/")) == NULL) 471 return (EG_CURROOT); 472 473 if ((zfh = zfs_open(fs->gf_lzfh, name, ZFS_TYPE_FILESYSTEM)) == 474 NULL) 475 return (EG_OPENZFS); 476 477 /* 478 * get_zfs_root returns non-zero on failure, not errno. 479 */ 480 if (get_zfs_root(zfh, fs, root)) 481 rc = EG_CURROOT; 482 else 483 /* 484 * For mirrored root physpath would contain the list of 485 * all bootable devices, pick up the first one. 486 */ 487 rc = get_one_physpath(root->gr_physpath, SLCNUM_INVALID, 488 PRTNUM_INVALID); 489 490 zfs_close(zfh); 491 492 } else if (strcmp(mp.mnt_fstype, MNTTYPE_UFS) == 0) { 493 (void) strlcpy(root->gr_fs[GRBM_UFS].gfs_dev, mp.mnt_special, 494 sizeof (root->gr_fs[GRBM_UFS].gfs_dev)); 495 (void) strlcpy(root->gr_fs[GRBM_UFS].gfs_mountp, mp.mnt_mountp, 496 sizeof (root->gr_fs[GRBM_UFS].gfs_mountp)); 497 } else { 498 rc = EG_UNKNOWNFS; 499 } 500 501 return (rc); 502 } 503 504 grub_fsdesc_t * 505 grub_get_rootfsd(const grub_root_t *root) 506 { 507 grub_fsdesc_t *fsd = NULL; 508 509 assert(root); 510 if (strcmp(MNTTYPE_UFS, root->gr_fstyp) == 0) 511 fsd = (grub_fsdesc_t *)root->gr_fs + GRBM_UFS; 512 else if (strcmp(MNTTYPE_ZFS, root->gr_fstyp) == 0) 513 fsd = (grub_fsdesc_t *)root->gr_fs + GRBM_ZFS_BOOTFS; 514 515 return (fsd); 516 } 517 518 /* 519 * Gets file systems mount point if any. 520 * Return 0 if filesystem is mounted, errno on failure. 521 */ 522 int 523 grub_fsd_get_mountp(grub_fsdesc_t *fsd, char *fstyp) 524 { 525 int rc; 526 FILE *fp = NULL; 527 struct mnttab mp = {0}; 528 struct mnttab mpref = {0}; 529 530 fsd->gfs_mountp[0] = 0; 531 532 if ((fp = fopen(MNTTAB, "r")) == NULL) 533 return (errno); 534 535 mpref.mnt_special = fsd->gfs_dev; 536 mpref.mnt_fstype = fstyp; 537 538 if ((rc = getmntany(fp, &mp, &mpref)) == 0) 539 (void) strlcpy(fsd->gfs_mountp, mp.mnt_mountp, 540 sizeof (fsd->gfs_mountp)); 541 else 542 rc = EG_GETMNTTAB; 543 544 (void) fclose(fp); 545 return (rc); 546 } 547 548 static const char tmp_mountp[] = "/tmp/.libgrubmgmt.%s.XXXXXX"; 549 550 /* 551 * Mount file system at tmp_mountp. 552 * Return 0 on success, errno on failure. 553 */ 554 int 555 grub_fsd_mount_tmp(grub_fsdesc_t *fsd, const char *fstyp) 556 { 557 const char *pos; 558 void *data = NULL; 559 int dtsz = 0; 560 struct ufs_args ufs_args = {UFSMNT_LARGEFILES}; 561 char mntopts[MNT_LINE_MAX] = ""; 562 int rc = 0; 563 564 assert(fsd); 565 assert(!fsd->gfs_is_tmp_mounted); 566 567 fsd->gfs_mountp[0] = 0; 568 569 if (strcmp(fstyp, MNTTYPE_UFS) == 0) { 570 (void) strlcpy(mntopts, MNTOPT_LARGEFILES, sizeof (mntopts)); 571 data = &ufs_args; 572 dtsz = sizeof (ufs_args); 573 } else if (strcmp(fstyp, MNTTYPE_ZFS) != 0) { 574 return (EG_UNKNOWNFS); 575 } 576 577 /* construct name for temporary mount point */ 578 pos = strrchr(fsd->gfs_dev, '/'); 579 pos = (pos == NULL) ? fsd->gfs_dev : pos + 1; 580 581 (void) snprintf(fsd->gfs_mountp, sizeof (fsd->gfs_mountp), 582 tmp_mountp, pos); 583 if (mkdtemp(fsd->gfs_mountp) != NULL) { 584 if ((rc = mount(fsd->gfs_dev, fsd->gfs_mountp, 585 MS_DATA | MS_OPTIONSTR | MS_RDONLY, 586 fstyp, data, dtsz, mntopts, sizeof (mntopts))) != 0) { 587 /* 588 * mount failed, collect errno and remove temp dir 589 */ 590 rc = errno; 591 (void) rmdir(fsd->gfs_mountp); 592 } 593 } else { 594 rc = errno; 595 } 596 597 if (rc != 0) 598 fsd->gfs_mountp[0] = 0; 599 600 /* 601 * Note that valid values for gfs_is_tmp_mounted are 0,1. 602 * Any other value indicates that something bad happened. 603 * Probably grub_fsd_umount_tmp() wasn't called or didn't 604 * work as expected. 605 */ 606 fsd->gfs_is_tmp_mounted += (rc == 0); 607 return (rc); 608 } 609 610 /* 611 * Unmount file system at tmp_mountp. 612 */ 613 void 614 grub_fsd_umount_tmp(grub_fsdesc_t *fsd) 615 { 616 if (fsd == NULL) 617 return; 618 619 if (fsd->gfs_is_tmp_mounted) { 620 if (fsd->gfs_mountp[0] != 0) { 621 (void) umount2(fsd->gfs_mountp, 0); 622 (void) rmdir(fsd->gfs_mountp); 623 fsd->gfs_mountp[0] = 0; 624 } 625 fsd->gfs_is_tmp_mounted = 0; 626 } 627 } 628